PageRenderTime 40ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/framework/FoundationModel.php

https://github.com/developmentcollective/framework
PHP | 422 lines | 333 code | 19 blank | 70 comment | 20 complexity | 97c93e3bee71175f95a28409a6f7c7ca MD5 | raw file
  1. <?php
  2. /**
  3. * All model objects created by the developer should extend this class
  4. * It provides utlity functions such as save() and validate()
  5. *
  6. * @package framework
  7. * @author simondelliott <simon@simondelliott.com>
  8. * @version @@@version
  9. */
  10. class FoundationModel {
  11. /**
  12. * a cache of properties that have been initalized by __get magic function
  13. * @var Object a cache for the properties for this model object
  14. */
  15. private $property_cache = null;
  16. /**
  17. * the table associated with the model object
  18. * @var type
  19. */
  20. private $_table_name = null;
  21. /** an array of the errors from the last time that the object was validated - only one entry per field */
  22. public $_validate_errors = array();
  23. /**
  24. * determines if the model object has been persisted.
  25. * requires the id field to be set.
  26. * @return boolean
  27. */
  28. public function has_been_saved (){
  29. if( $this->id == null ){
  30. return false;
  31. }
  32. elseif( $this->id == 0 ){
  33. return false;
  34. }
  35. else{
  36. return true;
  37. }
  38. }
  39. /**
  40. * Constructor for all the Model objects
  41. * It accepts an associative array of NVP's
  42. * It then matches these to model properties and populates the object.
  43. *
  44. * @param $obj the assocative array of NVPs for the properties
  45. */
  46. public function __construct($obj=NULL){
  47. if ($obj!=NULL){
  48. $properties = get_object_vars($this);
  49. foreach($properties as $k=>$v){
  50. if($obj[$k]!=NULL){
  51. $this->$k = $obj[$k];
  52. }
  53. }
  54. }
  55. $this->_table_name = strtolower(get_class($this));
  56. }
  57. /**
  58. * validates that the peoprty is not null
  59. *
  60. * @return Boolean the validation success
  61. */
  62. public function validate_not_null($thing_to_vaidate){
  63. $properties = get_object_vars($this);
  64. $value = $properties[$thing_to_vaidate];
  65. if ($value == NULL ){
  66. $this->_validate_errors[$thing_to_vaidate] = $thing_to_vaidate . " must be supplied";
  67. return false;
  68. }
  69. return true;
  70. }
  71. public function validate_made_by_current_user(){
  72. if ($this->user_id != User::get_current_user()->id ){
  73. $this->_validate_errors["user_id"] = "Only current user is allowed to change this";
  74. return false;
  75. }
  76. return true;
  77. }
  78. /**
  79. * validates the length of a field against a set of conditions
  80. * conditions are
  81. * <ul>
  82. * <li>less_than</li>
  83. * <li>greater_than</li>
  84. * </ul>
  85. *
  86. * @param string $thing_to_vaidate
  87. * @param array $condition
  88. * @return boolean
  89. */
  90. public function validate_length_of ($thing_to_vaidate, $condition){
  91. $properties = get_object_vars($this);
  92. $value = $properties[$thing_to_vaidate];
  93. foreach ($condition as $k=>$v) {
  94. if ($k == "less_than" && strlen($value) >= $v){
  95. $this->_validate_errors[$thing_to_vaidate] = $thing_to_vaidate . " must be shorter than " . $v;
  96. return false;
  97. }
  98. elseif ($k == "greater_than" && strlen($value) <= $v){
  99. $this->_validate_errors[$thing_to_vaidate] = $thing_to_vaidate . " must be longer than " . $v . " characters";
  100. return false;
  101. }
  102. elseif ($k == "greater_than_or_equal_to" && strlen($value) < $v){
  103. $this->_validate_errors[$thing_to_vaidate] = $thing_to_vaidate . " must be longer than " . $v . " characters";
  104. return false;
  105. }
  106. }
  107. return true;
  108. }
  109. /**
  110. * validates that the field conforms to a regex pattern
  111. * If the field fails validation then the error message is added to the
  112. * _vaidate_errors array
  113. *
  114. * @return boolean wheter the validation passed
  115. * @param $thing_to_vaidate string the filed that is to be validated
  116. * @param $pattern the regex pattern to validate against
  117. * @param $message the error message to show is the validation fails
  118. */
  119. public function validate_pattern($thing_to_vaidate, $pattern, $message ){
  120. $properties = get_object_vars($this);
  121. $value = $properties[$thing_to_vaidate];
  122. $x = preg_match($pattern, $value);
  123. if ($x == 0){
  124. $this->_validate_errors[$thing_to_vaidate] = $message;
  125. return false;
  126. }
  127. return true;
  128. }
  129. /**
  130. * validates that the fields are the same
  131. * If the field fails validation then the error message is added to the
  132. * _vaidate_errors array
  133. *
  134. * @return boolean whether the validation passed
  135. * @param $first - first field
  136. * @param $second - second field
  137. * @param $message the error message to show is the validation fails
  138. */
  139. public function validate_same($first, $second, $compareto, $message ){
  140. $properties = get_object_vars($this);
  141. $value1 = $properties[$first];
  142. if (strcmp($value1, $compareto) != 0){
  143. $this->_validate_errors[$second] = $message;
  144. return false;
  145. }
  146. return true;
  147. }
  148. /**
  149. * @param $thing_to_vaidate object the filed that you want to ensure is unique
  150. * @param $message the message to display if it is not
  151. */
  152. public function validate_uniqueness_of ($thing_to_vaidate, $message){
  153. $properties = get_object_vars($this);
  154. $value = $properties[$thing_to_vaidate];
  155. $database=new Database();
  156. $row = null;
  157. $value = str_replace("'", "\'", $value);
  158. $SQL = "SELECT id FROM " . $this->_table_name . " WHERE " . $thing_to_vaidate . "='" . $value . "' AND id != '" . $this->id . "' LIMIT 1";
  159. $database->setQuery ( $SQL);
  160. if (!$database->query()){
  161. handle_error("unexpected SQL error", E_USER_ERROR);
  162. return false;
  163. }
  164. $rows = $database->loadRowList();
  165. unset($database);
  166. if ($rows[0] == NULL){
  167. return true;
  168. }
  169. else{
  170. $this->_validate_errors[$thing_to_vaidate] = $message;
  171. return false;
  172. }
  173. }
  174. public function validate_unique_combination ($things_to_vaidate, $message){
  175. //TODO: refactor this into validate uniqueness of
  176. $properties = get_object_vars($this);
  177. $where = "";
  178. foreach ($things_to_vaidate as $k) {
  179. $where = $k . "='" . $properties[$k] ."' AND ";
  180. }
  181. $database=new Database();
  182. $row = null;
  183. $SQL = "SELECT id FROM " . $this->_table_name . " WHERE " . $where . " id != '" . $this->id . "' LIMIT 1";
  184. $database->setQuery ( $SQL);
  185. if (!$database->query()){
  186. handle_error("unexpected SQL error", E_USER_ERROR);
  187. return false;
  188. }
  189. $rows = $database->loadRowList();
  190. unset($database);
  191. if ($rows[0] == NULL){
  192. return true;
  193. }
  194. else{
  195. $this->_validate_errors[ implode(",",$things_to_vaidate) ] = $message;
  196. return false;
  197. }
  198. }
  199. /**
  200. * persists the data in the sub class to the database
  201. * the name of the subclass must correspond to an object name in the database.
  202. *
  203. * @return boolean status of the call.
  204. */
  205. public function save(){
  206. $ret = false;
  207. if (method_exists($this, "validate") && !$this->validate()){
  208. return false;
  209. }
  210. $database=new Database();
  211. if ($this->id == NULL || $this->id == 0){
  212. // its a new object
  213. $ret = $database->insertObject( $this->_table_name, $this, "id");
  214. }
  215. else{
  216. $ret = $database->updateObject( $this->_table_name, $this, "id");
  217. }
  218. if(!$ret){
  219. handle_error('save failed: ' . $database->getErrorNum() . ":" . $database->getErrorMsg(), E_USER_ERROR);
  220. return false;
  221. }
  222. return $ret;
  223. }
  224. /**
  225. * removes an object from the database
  226. *
  227. * @return Boolean the success of the call, from teh database object
  228. */
  229. public function delete(){
  230. $database=new Database();
  231. if ($this->id != NULL && $this->id != 0){
  232. $SQL = "DELETE FROM " . $this->_table_name . " WHERE id=" . $this->id;
  233. $database->setQuery ( $SQL);
  234. return $database->query();
  235. }
  236. }
  237. public function __get($name) {
  238. if ($this->property_cache[$name] != NULL ) {
  239. return $this->property_cache[$name];
  240. }
  241. $find_result = null;
  242. $model_class_name = ucwords($name);
  243. $property_name = $name ."_id";
  244. $property_exists = property_exists( $this, $property_name );
  245. if(property_exists ( $this, $property_name )){
  246. //look for an _id field if it exists initalise the object and return it
  247. $find_call = "return " . $model_class_name . "::find_by_id(array(id => " . $this->$property_name ."));";
  248. $find_result = eval ($find_call);
  249. }
  250. elseif (class_exists( $model_class_name ) ){
  251. //check for an object class of this name, it it has a [table_name]_id field then get them
  252. $filed_name = $this->_table_name . "_id";
  253. $id_to_find = $this->id == null ? "NULL": $this->id;
  254. $find_call = "return " . $model_class_name . "::find_by_" . $filed_name. "(array('$filed_name'=>" . $id_to_find . "));";
  255. //debug("find_call is $find_call");
  256. $find_result = eval ($find_call);
  257. }
  258. else{
  259. // if nothing exists, so generate an error
  260. handle_error("undefined property", E_USER_ERROR);
  261. return null;
  262. }
  263. //Add the result to the cache
  264. $this->property_cache[$name] = $find_result;
  265. //return the cache
  266. return $this->property_cache[$name];
  267. }
  268. public function __set($name, $value) {
  269. if ($this->property_cache[$name] != NULL ) {
  270. $this->property_cache[$name] = $value;
  271. }
  272. $model_class_name = ucwords($name);
  273. $property_name = $name ."_id";
  274. $property_exists = property_exists( $this, $property_name );
  275. if(property_exists ( $this, $property_name && $this->_table_name == $model_class_name )){
  276. $this->property_cache[$name] = $value;
  277. $this->$property_name = $value->id;
  278. }
  279. elseif (class_exists( $model_class_name )){
  280. $filed_name = strtolower($this->_table_name) . "_id";
  281. $this->property_cache[$name] = $value;
  282. foreach($value as $obj){
  283. $obj->$filed_name = $this->id;
  284. }
  285. }
  286. else{
  287. handle_error('Setting undefined property via __set(): ' . $name, E_USER_NOTICE);
  288. return null;
  289. }
  290. }
  291. /**
  292. * expresses the model object as a JSON string
  293. *
  294. * @return string the object.
  295. */
  296. public function toJSON(){
  297. $json = "{";
  298. $properties = get_object_vars($this);
  299. foreach ($properties as $k=>$v) {
  300. if (is_array($v) or is_object($v) or $v === NULL) {
  301. continue;
  302. }
  303. if ($k[0] == '_') { // internal field
  304. continue;
  305. }
  306. $json .= '"' . $k . '":';
  307. $typ = gettype($v);
  308. if ($typ=="string"){
  309. $json .= '"' . $v . '"';
  310. }
  311. else{
  312. $json .= $v;
  313. }
  314. $json .= ', ';
  315. }
  316. if(strlen($json)>1){
  317. $json = substr($json, 0,strlen($json) -2); //remove the comma and the space
  318. }
  319. $json .= "}";
  320. return $json;
  321. }
  322. /**
  323. * Export item list to xml
  324. * @param boolean Map foreign keys to text values
  325. */
  326. function toXML( ) {
  327. $node_name = $this->_table_name;
  328. $xml = "<$node_name>";
  329. foreach (get_object_vars( $this ) as $k => $v) {
  330. if (is_array($v) or is_object($v) or $v === NULL) {
  331. continue;
  332. }
  333. if ($k[0] == '_') { // internal field
  334. continue;
  335. }
  336. $xml .= '<' . $k . '><![CDATA[' . $v . ']]></' . $k . '>';
  337. }
  338. $xml .= '</' . $node_name . '>';
  339. return $xml;
  340. }
  341. /**
  342. * Export item list to xml
  343. * @param boolean Map foreign keys to text values
  344. */
  345. function toTR( ) {
  346. $ret = "<tr>";
  347. foreach (get_object_vars( $this ) as $k => $v) {
  348. if (is_array($v) or is_object($v) or $v === NULL) {
  349. continue;
  350. }
  351. if ($k[0] == '_') { // internal field
  352. continue;
  353. }
  354. $ret .= "<td>$k $v</td>";
  355. }
  356. $ret .= "</tr>";
  357. return $ret;
  358. }
  359. function output_as_xml(){
  360. set_xml_header();
  361. echo $this->toXML();
  362. }
  363. }
  364. ?>