PageRenderTime 48ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/MiniMVC/MiniMVC/Model.php

https://github.com/tquensen/MiniMVC
PHP | 891 lines | 693 code | 74 blank | 124 comment | 179 complexity | 94ea0cd658f22346fd10891dfda1f0c4 MD5 | raw file
  1. <?php
  2. class MiniMVC_Model implements ArrayAccess, Serializable
  3. {
  4. protected $_properties = array();
  5. protected $_databaseProperties = array();
  6. protected $_relations = array();
  7. protected $_table = null;
  8. protected $_collection = null;
  9. public function __construct($table = null)
  10. {
  11. if ($table) {
  12. $this->_table = $table;
  13. }
  14. }
  15. public function getIdentifier()
  16. {
  17. return $this->{$this->getTable()->getIdentifier()};
  18. }
  19. /**
  20. *
  21. * @return MiniMVC_Table
  22. */
  23. public function getTable()
  24. {
  25. if ($this->_table === null) {
  26. $tableName = get_class($this).'Table';
  27. $this->_table = call_user_func($tableName . '::getInstance');
  28. }
  29. return $this->_table;
  30. }
  31. /**
  32. *
  33. * @return MiniMVC_Collection
  34. */
  35. public function getCollection()
  36. {
  37. if (!$this->_collection) {
  38. $this->_collection = $this->getTable()->getCollection();
  39. }
  40. return $this->_collection;
  41. }
  42. public function setCollection($collection) {
  43. if (is_object($collection) && $collection instanceof MiniMVC_Collection) {
  44. $this->_collection = $collection;
  45. return true;
  46. }
  47. throw new InvalidArgumentException('$collection must be a MiniMVC_Collection instance!');
  48. }
  49. public function isNew()
  50. {
  51. return empty($this->_databaseProperties);
  52. }
  53. public function getDatabaseProperty($key)
  54. {
  55. return isset($this->_databaseProperties[$key]) ? $this->_databaseProperties[$key] : null;
  56. }
  57. public function setDatabaseProperty($key, $value)
  58. {
  59. $this->_databaseProperties[$key] = $value;
  60. }
  61. public function clearDatabaseProperties()
  62. {
  63. $this->_databaseProperties = array();
  64. }
  65. public function __get($key)
  66. {
  67. if ($this->getTable()->getRelation($key)) {
  68. return $this->getRelated($key);
  69. }
  70. return isset($this->_properties[$key]) ? $this->_properties[$key] : null;
  71. }
  72. public function __set($key, $value)
  73. {
  74. if ($this->getTable()->getRelation($key)) {
  75. $this->setRelated($key, $value);
  76. } else {
  77. $this->_properties[$key] = $value;
  78. }
  79. }
  80. public function __isset($key)
  81. {
  82. if ($this->getTable()->getRelation($key)) {
  83. return isset($this->_relations[$key]);
  84. }
  85. return isset($this->_properties[$key]);
  86. }
  87. public function __unset($key)
  88. {
  89. if ($this->getTable()->getRelation($key)) {
  90. $this->unlinkRelated($key);
  91. } elseif (isset($this->_properties[$key])) {
  92. unset($this->_properties[$key]);
  93. }
  94. }
  95. public function offsetSet($offset, $data)
  96. {
  97. if ($offset === null) {
  98. return;
  99. }
  100. if ($this->getTable()->getRelation($offset)) {
  101. $this->setRelated($offset, $data);
  102. } else {
  103. $this->_properties[$offset] = $data;
  104. }
  105. }
  106. public function offsetGet($offset)
  107. {
  108. if ($this->getTable()->getRelation($offset)) {
  109. return $this->getRelated($offset);
  110. }
  111. return isset($this->_properties[$offset]) ? $this->_properties[$offset] : null;
  112. }
  113. public function offsetExists($offset)
  114. {
  115. if ($this->getTable()->getRelation($offset)) {
  116. return isset($this->_relations[$offset]);
  117. }
  118. return isset($this->_properties[$offset]);
  119. }
  120. public function offsetUnset($offset)
  121. {
  122. if ($this->getTable()->getRelation($offset)) {
  123. $this->unlinkRelated($offset);
  124. } elseif (isset($this->_properties[$offset])) {
  125. unset($this->_properties[$offset]);
  126. }
  127. }
  128. public function serialize()
  129. {
  130. return serialize(array(
  131. 'p' => $this->_properties,
  132. 'dbp' => $this->_databaseProperties,
  133. 'rel' => $this->_relations,
  134. 'col' => $this->_collection
  135. ));
  136. }
  137. public function unserialize($serialized)
  138. {
  139. $data = unserialize($serialized);
  140. $this->__construct();
  141. $this->_properties = $data['p'];
  142. $this->_databaseProperties = $data['dbp'];
  143. $this->_relations = $data['rel'];
  144. $this->_collection = $data['col'];
  145. }
  146. /**
  147. *
  148. * examples:
  149. * $fields could look like the following:
  150. * array(
  151. * 'id', 'title', 'description'
  152. * )
  153. * or
  154. * array(
  155. * 'id', 'title', 'description', 'relations' => array(
  156. * 'Comments' => true, //export all fields of the related comment model
  157. * 'User' => array('id', 'username', 'email'), //export the id, username and email of the related users
  158. * 'Tags' => array(
  159. * 'id', 'title', 'relations' => array( //you can also fetch relations of relations
  160. * 'Posts' => array('id') //get the post-ids related to each tag
  161. * )
  162. * )
  163. * )
  164. * )
  165. *
  166. * @param array|bool $fields true to export all model properties or an array of property-names to export. add a key 'relations' with a value structured like this (true or array) to include related models
  167. * @return array
  168. */
  169. public function toArray($fields = true)
  170. {
  171. $return = array();
  172. foreach ($this->getTable()->getColumns() as $column) {
  173. if ($fields === true || (is_array($fields) && in_array($column, $fields))) {
  174. $return[$column] = $this->$column;
  175. }
  176. }
  177. if (!empty($fields['relations'])) {
  178. foreach ($this->getTable()->getRelations() as $relation => $relationData) {
  179. if ($fields['relations'] === true || (is_array($fields['relations']) && isset($fields['relations'][$relation]))) {
  180. $currentRelationEntries = $this->getRelated($relation, true, false);
  181. if (is_array($currentRelationEntries) || (is_object($currentRelationEntries) && $currentRelationEntries instanceof MiniMVC_Collection)) {
  182. foreach ($currentRelationEntries as $k => $v) {
  183. $currentRelationEntries[$k] = $v->toArray($fields['relations'] === true ? true : $fields['relations'][$relation]);
  184. }
  185. } elseif(is_object($currentRelationEntries)) {
  186. $currentRelationEntries = $currentRelationEntries->toArray($fields['relations'] === true ? true : $fields['relations'][$relation]);
  187. }
  188. $return[$relation] = $currentRelationEntries;
  189. }
  190. }
  191. }
  192. return $return;
  193. }
  194. public function __call($name, $arguments)
  195. {
  196. if (!preg_match('/^(get|set|delete|load|save|unlink|link)([\w]+)$/', $name, $matches)) {
  197. throw new Exception('unknown Method '.$name.' in class '.get_class($this));
  198. }
  199. $fnc = $matches[1].'Related';
  200. $relation = $matches[2];
  201. array_unshift($arguments, $relation);
  202. return call_user_func_array(array($this, $fnc), $arguments);
  203. /*
  204. if (substr($name, 0, 3) == 'get') {
  205. $relation = strtolower(substr($name, 3));
  206. $identifier = isset($arguments[0]) ? $arguments[0] : true;
  207. return $this->getRelated($relation, $identifier);
  208. } elseif (substr($name, 0, 3) == 'set') {
  209. $relation = strtolower(substr($name, 3));
  210. $identifier = isset($arguments[0]) ? $arguments[0] : null;
  211. $update = isset($arguments[1]) ? $arguments[1] : true;
  212. return $this->setRelated($relation, $identifier, $update);
  213. } elseif (substr($name, 0, 6) == 'delete') {
  214. $relation = strtolower(substr($name, 6));
  215. $identifier = isset($arguments[0]) ? $arguments[0] : true;
  216. $realDelete = isset($arguments[1]) ? $arguments[1] : true;
  217. $realDeleteLoad = isset($arguments[2]) ? $arguments[2] : false;
  218. $realDeleteCleanRef = isset($arguments[3]) ? $arguments[3] : false;
  219. return $this->deleteRelated($relation, $identifier, $realDelete, $realDeleteLoad, $realDeleteCleanRef);
  220. } elseif (substr($name, 0, 4) == 'load') {
  221. $relation = strtolower(substr($name, 4));
  222. $condition = isset($arguments[0]) ? $arguments[0] : null;
  223. $values = isset($arguments[0]) ? $arguments[0] : array();
  224. $order = isset($arguments[1]) ? $arguments[1] : null;
  225. $limit = isset($arguments[2]) ? $arguments[2] : null;
  226. $offset = isset($arguments[3]) ? $arguments[3] : null;
  227. return $this->loadRelated($relation, $condition, $values, $order, $limit, $offset);
  228. } elseif(substr($name, 0, 4) == 'save') {
  229. $relation = strtolower(substr($name, 4));
  230. $identifier = isset($arguments[0]) ? $arguments[0] : true;
  231. return $this->saveRelated($relation, $identifier);
  232. } elseif(substr($name, 0, 4) == 'link') {
  233. $relation = strtolower(substr($name, 4));
  234. $identifier = isset($arguments[0]) ? $arguments[0] : null;
  235. return $this->linkRelated($relation, $identifier);
  236. } elseif(substr($name, 0, 6) == 'unlink') {
  237. $relation = strtolower(substr($name, 6));
  238. $identifier = isset($arguments[0]) ? $arguments[0] : true;
  239. return $this->unlinkRelated($relation, $identifier);
  240. }
  241. return null;
  242. */
  243. }
  244. /**
  245. *
  246. * @param string $relation the name of a relation
  247. * @param mixed $identifier the identifier of the related model or true to return all stored models of this relation
  248. * @param bool $load true (default) to load the related entries from db if not already there
  249. * @return MiniMVC_Model|array
  250. */
  251. public function getRelated($relation, $identifier = true, $load = true)
  252. {
  253. if (!$relationInfo = $this->getTable()->getRelation($relation)) {
  254. throw new Exception('Unknown relation "'.$relation.'" for model '.$this->getTable()->getModelName());
  255. }
  256. if ($identifier === true) {
  257. if (isset($this->_relations[$relation])) {
  258. return (isset($relationInfo[3]) && $relationInfo[3] === true) ? $this->_relations[$relation]->getFirst() : $this->_relations[$relation];
  259. } elseif ($load) {
  260. return $this->loadRelated($relation);
  261. }
  262. } else {
  263. if (isset($this->_relations[$relation]) && $entry = $this->_relations[$relation][$identifier]) {
  264. return $entry;
  265. } elseif ($load && !isset($this->_relations[$relation])) {
  266. $tableName = $relationInfo[0].'Table';
  267. $table = call_user_func($tableName . '::getInstance');
  268. return $this->loadRelated($relation, $table->getIdentifier().' = ?', $identifier);
  269. }
  270. }
  271. if ($identifier !== true || (isset($relationInfo[3]) && $relationInfo[3] === true)) {
  272. return null;
  273. }
  274. $collectionName = $relationInfo[0].'Collection';
  275. $collection = call_user_func($collectionName . '::getInstance');
  276. return $collection;
  277. }
  278. /**
  279. *
  280. * @param string $relation the name of a relation
  281. * @param MiniMVC_Model|array $identifier the related model or an array of models
  282. * @param bool $update whether to update the model if it is already stored or not
  283. */
  284. public function setRelated($relation, $identifier = null, $update = true)
  285. {
  286. if (is_array($identifier) || $identifier instanceof MiniMVC_Collection) {
  287. foreach ($identifier as $id) {
  288. $this->setRelated($relation, $id, $update);
  289. }
  290. return true;
  291. }
  292. if (is_object($identifier) && $identifier instanceof MiniMVC_Model) {
  293. $info = $this->getTable()->getRelation($relation);
  294. if (!$info) {
  295. throw new Exception('Unknown relation "'.$relation.'" for model '.$this->getTable()->getModelName());
  296. }
  297. if (!isset($this->_relations[$relation])) {
  298. $this->_relations[$relation] = $identifier->getTable()->getCollection();
  299. }
  300. if (!$identifier->getIdentifier()) {
  301. $this->_relations[$relation][] = $arguments[0];
  302. return true;
  303. }
  304. if ($update || !isset($this->_relations[$relation][$identifier->getIdentifier()])) {
  305. $this->_relations[$relation][$identifier->getIdentifier()] = $identifier;
  306. if (!isset($info[3]) || $info[3] === true) {
  307. if ($info[1] == $this->getTable()->getIdentifier()) {
  308. $identifier->{$info[2]} = $this->getIdentifier();
  309. } elseif ($info[2] == $identifier->getTable()->getIdentifier() && $identifier->getIdentifier()) {
  310. $this->{$info[1]} = $identifier->getIdentifier();
  311. }
  312. }
  313. }
  314. return true;
  315. }
  316. throw new InvalidArgumentException('$identifier must be a MiniMVC_Model instance!');
  317. }
  318. /**
  319. *
  320. * @param string $relation the name of a relation
  321. * @param mixed $identifier either a model object, an identifier of a related model or true
  322. * @param bool $realDelete whether to delete the model from the database (true) or just from this object(false) defaults to true
  323. * @param bool $realDeleteLoad if the identifier is true only the related models currently assigned to this object will be deleted. with relaDeleteLoad=true, all related models will be deleted
  324. * @param bool $realDeleteCleanRef if relaDeleteLoad is true, set realDeleteCleanRef=true to clean up the ref table (for m:n relations)
  325. */
  326. public function deleteRelated($relation, $identifier = true, $realDelete = true, $realDeleteLoad = false, $realDeleteCleanRef = false)
  327. {
  328. if (is_array($identifier)) {
  329. foreach ($identifier as $id) {
  330. $this->deleteRelated($relation, $id, $realDelete, $realDeleteLoad, $realDeleteCleanRef);
  331. }
  332. return true;
  333. }
  334. if (is_object($identifier)) {
  335. if (isset($this->_relations[$relation][$identifier->getIdentifier()])) {
  336. unset($this->_relations[$relation][$identifier->getIdentifier()]);
  337. if (!$data = $this->getTable()->getRelation($relation)) {
  338. throw new Exception('Unknown relation "'.$relation.'" for model '.$this->getTable()->getModelName());
  339. }
  340. if ($realDelete) {
  341. $identifier->delete();
  342. }
  343. if ($data[1] != $this->getTable->getIdentifier && (!isset($data[3]) || $data[3] === true)) {
  344. $this->{$data[1]} = null;
  345. }
  346. return true;
  347. }
  348. } else {
  349. if (!$data = $this->getTable()->getRelation($relation)) {
  350. throw new Exception('Unknown relation "'.$relation.'" for model '.$this->getTable()->getModelName());
  351. }
  352. if ($identifier === true) {
  353. if ($realDelete === true) {
  354. if ($realDeleteLoad === true) {
  355. $tableName = $data[0].'Table';
  356. $table = call_user_func($tableName . '::getInstance');
  357. if (isset($data[3]) && $data[3] !== true) {
  358. $stmt = $this->getTable()->query('a', false)->select('b.'.$table->getIdentifier())->join('a.'.$relation, 'b')->where('a.'.$this->getTable()->getIdentifier().' = ? AND b.'.$table->getIdentifier(). ' IS NOT NULL')->execute($this->getIdentifier());
  359. $relatedTableIds = $stmt->fetchAll(PDO::FETCH_COLUMN);
  360. //$this->getTable()->query('a', false)->delete('b')->join('a.'.$relation, 'b')->where('a.'.$this->getTable()->getIdentifier().' = ? AND b.'.$table->getIdentifier(). ' IS NOT NULL')->execute($this->getIdentifier());
  361. $deleteStmt = MiniMVC_Registry::getInstance()->db->query()->delete($table)->where($table->getIdentifier() . ' = ?')->prepare();
  362. foreach ($relatedTableIds as $relatedTableId) {
  363. $deleteStmt->execute(array($relatedTableId));
  364. }
  365. if ($realDeleteCleanRef) {
  366. $table->cleanRefTables();
  367. }
  368. } else {
  369. $table->deleteBy($data[2] . ' = ?', $this->{$data[1]}, $realDeleteCleanRef);
  370. }
  371. } else {
  372. foreach ($this->_relations[$relation] as $entry) {
  373. $entry->delete();
  374. }
  375. }
  376. }
  377. unset($this->_relations[$relation]);
  378. } else {
  379. if (isset($this->_relations[$relation][$identifier])) {
  380. if ($realDelete) {
  381. $this->_relations[$relation][$identifier]->delete();
  382. }
  383. unset($this->_relations[$relation][$identifier]);
  384. } elseif($realDeleteLoad === true) {
  385. $tableName = $data[0].'Table';
  386. $table = call_user_func($tableName . '::getInstance');
  387. $table->delete($identifier);
  388. }
  389. }
  390. if ($data[1] != $this->getTable()->getIdentifier() && (!isset($data[3]) || $data[3] === true)) {
  391. $this->{$data[1]} = null;
  392. }
  393. return true;
  394. }
  395. }
  396. /**
  397. *
  398. * @param string $relation the name of a relation
  399. * @param string $condition the where-condition
  400. * @param array $values values for the placeholders in the condition
  401. * @param string $order the order
  402. * @param int $limit the limit
  403. * @param int $offset the offset
  404. * @return MiniMVC_Model|MiniMVC_Collection
  405. */
  406. public function loadRelated($relation, $condition = null, $values = array(), $order = null, $limit = null, $offset = null)
  407. {
  408. if (!$data = $this->getTable()->getRelation($relation)) {
  409. throw new Exception('Unknown relation "'.$relation.'" for model '.$this->getTable()->getModelName());
  410. }
  411. if (!is_array($values)) {
  412. $values = (array) $values;
  413. }
  414. if (isset($data[3]) && $data[3] !== true) {
  415. array_unshift($values, $this->getIdentifier());
  416. $tableName = $data[0].'Table';
  417. $table = call_user_func($tableName . '::getInstance');
  418. $query = $this->getTable()->query('b', 'a')->join('b.'.$relation, 'a')->where('b.'.$this->getTable()->getIdentifier().' = ? AND a.'.$table->getIdentifier(). ' IS NOT NULL');
  419. if ($condition) {
  420. $query->where($condition);
  421. }
  422. $query->orderBy($order)->limit($limit, $offset);
  423. $entries = $query->build($values);
  424. } else {
  425. array_unshift($values, $this->{$data[1]});
  426. $where = $data[2].' = ?' . ($condition ? ' AND '.$condition : '');
  427. $tableName = $data[0].'Table';
  428. $table = call_user_func($tableName . '::getInstance');
  429. $entries = $table->load($where, $values, $order, $limit, $offset);
  430. }
  431. if (isset($this->_relations[$relation])) {
  432. $this->_relations[$relation]->add($entries);
  433. } else {
  434. $this->_relations[$relation] = $entries;
  435. }
  436. return (isset($data[3]) && $data[3] === true) ? $entries->getFirst() : $entries;
  437. }
  438. /**
  439. *
  440. * @param string $relation the name of a relation
  441. * @param mixed $identifier a related model object, the identifier of a related model currently asigned to this model or true to save all related models
  442. * @param bool $saveThisOnDemand whether to allow this model to be saved in the database if its new (to generate an auto-increment identifier)
  443. */
  444. public function saveRelated($relation, $identifier = true, $saveThisOnDemand = true)
  445. {
  446. if (is_array($identifier)) {
  447. foreach ($identifier as $id) {
  448. $this->saveRelated($relation, $id, $saveThisOnDemand);
  449. }
  450. return true;
  451. }
  452. if (!$info = $this->getTable()->getRelation($relation)) {
  453. throw new Exception('Unknown relation "'.$relation.'" for model '.$this->getTable()->getModelName());
  454. }
  455. //$this->save();
  456. $tableName = $info[0].'Table';
  457. $table = call_user_func($tableName . '::getInstance');
  458. if (!isset($info[3]) || $info[3] === true) {
  459. if ($info[1] == $this->getTable()->getIdentifier()) {
  460. if (!$this->getIdentifier()) {
  461. if (!$saveThisOnDemand) {
  462. return false;
  463. }
  464. $this->save();
  465. }
  466. if ($identifier === true) {
  467. if (!isset($this->_relations[$relation])) {
  468. return false;
  469. }
  470. foreach ($this->_relations[$relation] as $relKey => $relation) {
  471. $relation->{$info[2]} = $this->getIdentifier();
  472. $relation->save();
  473. if (is_numeric($relKey) && $relKey < 0) {
  474. $this->_relations[$relation][$relation->getIdentifier()] = $relation;
  475. unset($this->_relations[$relation][$relKey]);
  476. }
  477. }
  478. } elseif (is_object($identifier)) {
  479. $identifier->{$info[2]} = $this->getIdentifier();
  480. $identifier->save();
  481. if (!isset($this->_relations[$relation])) {
  482. $this->_relations[$relation] = $identifier->getTable()->getCollection();
  483. }
  484. $this->_relations[$relation][$identifier->getIdentifier()] = $identifier;
  485. } elseif (isset($this->_relations[$relation][$identifier])) {
  486. $this->_relations[$relation][$identifier]->{$info[2]} = $this->getIdentifier();
  487. $this->_relations[$relation][$identifier]->save();
  488. }
  489. } elseif ($info[2] == $table->getIdentifier()) {
  490. if ($identifier === true) {
  491. if (!isset($this->_relations[$relation])) {
  492. return false;
  493. }
  494. foreach ($this->_relations[$relation] as $relKey => $relation) {
  495. $relation->save();
  496. $this->{$info[1]} = $relation->getIdentifier();
  497. if (is_numeric($relKey) && $relKey < 0) {
  498. $this->_relations[$relation][$relation->getIdentifier()] = $relation;
  499. unset($this->_relations[$relation][$relKey]);
  500. }
  501. }
  502. } elseif (is_object($identifier)) {
  503. $identifier->save();
  504. $this->{$info[1]} = $identifier->getIdentifier();
  505. if (!isset($this->_relations[$relation])) {
  506. $this->_relations[$relation] = $identifier->getTable()->getCollection();
  507. }
  508. $this->_relations[$relation][$identifier->getIdentifier()] = $identifier;
  509. } elseif (isset($this->_relations[$relation][$identifier])) {
  510. $this->_relations[$relation][$identifier]->save();
  511. $this->{$info[1]} = $this->_relations[$relation][$identifier]->getIdentifier();
  512. }
  513. }
  514. } else {
  515. if ($identifier === true) {
  516. if (!isset($this->_relations[$relation])) {
  517. return false;
  518. }
  519. if (!$this->getIdentifier()) {
  520. if (!$saveThisOnDemand) {
  521. return false;
  522. }
  523. $this->save();
  524. }
  525. $stmt = MiniMVC_Registry::getInstance()->db->query()->select('id, '.$info[1].', '.$info[2])->from($info[3])->where($info[1].' = ?')->execute($this->getIdentifier());
  526. $rows = array();
  527. foreach ($stmt->fetchAll(PDO::FETCH_NUM) as $row) {
  528. $rows[$row[2]] = $row[1];
  529. }
  530. foreach ($this->_relations[$relation] as $relation) {
  531. $relation->save();
  532. if ($relation->getIdentifier() && !isset($rows[$relation->getIdentifier()])) {
  533. MiniMVC_Registry::getInstance()->db->query()->insert(array($info[1], $info[2]), array($this->getIdentifier(), $relation->getIdentifier()), $info[3])->execute();
  534. }
  535. }
  536. } elseif (is_object($identifier)) {
  537. $identifier->save();
  538. $stmt = MiniMVC_Registry::getInstance()->db->query()->select('id, '.$info[1].', '.$info[2])->from($info[3])->where($info[1].' = ? AND '.$info[2].' = ?')->execute(array($this->getIdentifier(), $identifier->getIdentifier()));
  539. $result = $stmt->fetch(PDO::FETCH_NUM);
  540. $stmt->closeCursor();
  541. if (!$result) {
  542. MiniMVC_Registry::getInstance()->db->query()->insert(array($info[1], $info[2]), array($this->getIdentifier(), $identifier->getIdentifier()), $info[3])->execute();
  543. }
  544. if (!isset($this->_relations[$relation])) {
  545. $this->_relations[$relation] = $identifier->getTable()->getCollection();
  546. }
  547. $this->_relations[$relation][$identifier->getIdentifier()] = $identifier;
  548. } elseif (isset($this->_relations[$relation][$identifier])) {
  549. $this->_relations[$relation][$identifier]->save();
  550. $stmt = MiniMVC_Registry::getInstance()->db->query()->select('id, '.$info[1].', '.$info[2])->from($info[3])->where($info[1].' = ? AND '.$info[2].' = ?')->execute(array($this->getIdentifier(), $this->_relations[$relation]['_'.$identifier]->getIdentifier()));
  551. $result = $stmt->fetch(PDO::FETCH_NUM);
  552. $stmt->closeCursor();
  553. if (!$result) {
  554. MiniMVC_Registry::getInstance()->db->query()->insert(array($info[1], $info[2]), array($this->getIdentifier(), $this->_relations[$relation]['_'.$identifier]->getIdentifier()), $info[3])->execute();
  555. }
  556. }
  557. }
  558. }
  559. /**
  560. *
  561. * @param string $relation the name of a relation
  562. * @param mixed $identifier a related model object, the identifier of a related model
  563. * @param bool $loadRelated whether to load the related object (if identifier is not already loaded and assigned to this model)
  564. */
  565. public function linkRelated($relation, $identifier = null, $loadRelated = false)
  566. {
  567. if (is_array($identifier)) {
  568. foreach ($identifier as $id) {
  569. $this->linkRelated($relation, $id, $loadRelated);
  570. }
  571. return true;
  572. }
  573. if (!$identifier) {
  574. throw new Exception('No identifier/related '.$relation.' given for model '.$this->getTable()->getModelName());
  575. }
  576. if (!$info = $this->getTable()->getRelation($relation)) {
  577. throw new Exception('Unknown relation "'.$relation.'" for model '.$this->getTable()->getModelName());
  578. }
  579. $tableName = $info[0].'Table';
  580. $table = call_user_func($tableName . '::getInstance');
  581. if (!isset($info[3]) || $info[3] === true) {
  582. if ($info[1] == $this->getTable()->getIdentifier()) {
  583. if (!$this->getIdentifier()) {
  584. $this->save();
  585. }
  586. if (is_object($identifier)) {
  587. $identifier->{$info[2]} = $this->getIdentifier();
  588. $identifier->save();
  589. if (!isset($this->_relations[$relation])) {
  590. $this->_relations[$relation] = $identifier->getTable()->getCollection();
  591. }
  592. $this->_relations[$relation][$identifier->getIdentifier()] = $identifier;
  593. } elseif (isset($this->_relations[$relation][$identifier])) {
  594. $this->_relations[$relation][$identifier]->{$info[2]} = $this->getIdentifier();
  595. $this->_relations[$relation][$identifier]->save();
  596. } else {
  597. if ($loadRelated && $object = $table->loadOne($identifier)) {
  598. $object->{$info[2]} = $this->getIdentifier();
  599. $object->save();
  600. if (!isset($this->_relations[$relation])) {
  601. $this->_relations[$relation] = $object->getTable()->getCollection();
  602. }
  603. $this->_relations[$relation][$object->getIdentifier()] = $object;
  604. } else {
  605. $table->query()->update($info[2])->where($table->getIdentifier().' = ?')->execute(array($this->getIdentifier(), $identifier));
  606. //MiniMVC_Registry::getInstance()->db->query()->update($info[0], $info[2])->where($table->getIdentifier().' = ?')->execute(array($this->getIdentifier(), $identifier));
  607. }
  608. }
  609. } elseif ($info[2] == $table->getIdentifier()) {
  610. if (is_object($identifier)) {
  611. if (!$identifier->getIdentifier()) {
  612. $identifier->save();
  613. }
  614. $this->{$info[1]} = $identifier->getIdentifier();
  615. if (!isset($this->_relations[$relation])) {
  616. $this->_relations[$relation] = $identifier->getTable()->getCollection();
  617. }
  618. $this->_relations[$relation][$identifier->getIdentifier()] = $identifier;
  619. } elseif(isset($this->_relations[$relation][$identifier])) {
  620. if (! $this->_relations[$relation][$identifier]->getIdentifier()) {
  621. $this->_relations[$relation][$identifier]->save();
  622. }
  623. $this->{$info[1]} = $this->_relations[$relation][$identifier]->getIdentifier();
  624. } else {
  625. $this->{$info[1]} = $identifier;
  626. if ($loadRelated && $object = $table->loadOne($identifier)) {
  627. if (!isset($this->_relations[$relation])) {
  628. $this->_relations[$relation] = $object->getTable()->getCollection();
  629. }
  630. $this->_relations[$relation][$identifier] = $object;
  631. }
  632. }
  633. $this->save();
  634. }
  635. } else {
  636. if (is_object($identifier)) {
  637. if (!$this->getIdentifier()) {
  638. $this->save();
  639. }
  640. if (!$identifier->getIdentifier()) {
  641. $identifier->save();
  642. }
  643. $stmt = MiniMVC_Registry::getInstance()->db->query()->select('id, '.$info[1].', '.$info[2])->from($info[3])->where($info[1].' = ? AND '.$info[2].' = ?')->execute(array($this->getIdentifier(), $identifier->getIdentifier()));
  644. $result = $stmt->fetch(PDO::FETCH_NUM);
  645. $stmt->closeCursor();
  646. if (!$result) {
  647. MiniMVC_Registry::getInstance()->db->query()->insert(array($info[1], $info[2]), array($this->getIdentifier(), $identifier->getIdentifier()), $info[3])->execute();
  648. }
  649. if (!isset($this->_relations[$relation])) {
  650. $this->_relations[$relation] = $identifier->getTable()->getCollection();
  651. }
  652. $this->_relations[$relation][$identifier->getIdentifier()] = $identifier;
  653. } elseif (isset($this->_relations[$relation][$identifier])) {
  654. if (!$this->getIdentifier()) {
  655. $this->save();
  656. }
  657. if (!$this->_relations[$relation][$identifier]->getIdentifier()) {
  658. $this->_relations[$relation][$identifier]->save();
  659. }
  660. $stmt = MiniMVC_Registry::getInstance()->db->query()->select('id, '.$info[1].', '.$info[2])->from($info[3])->where($info[1].' = ? AND '.$info[1].' = ?')->execute(array($this->getIdentifier(), $this->_relations[$relation]['_'.$identifier]->getIdentifier()));
  661. $result = $stmt->fetch(PDO::FETCH_NUM);
  662. $stmt->closeCursor();
  663. if (!$result) {
  664. MiniMVC_Registry::getInstance()->db->query()->insert(array($info[1], $info[2]), array($this->getIdentifier(), $this->_relations[$relation]['_'.$identifier]->getIdentifier()), $info[3])->execute();
  665. }
  666. } else {
  667. if (!$this->getIdentifier()) {
  668. $this->save();
  669. }
  670. if ($loadRelated && $object = $table->loadOne($identifier)) {
  671. if (!isset($this->_relations[$relation])) {
  672. $this->_relations[$relation] = $object->getTable()->getCollection();
  673. }
  674. $this->_relations[$relation][$identifier] = $object;
  675. }
  676. $stmt = MiniMVC_Registry::getInstance()->db->query()->select('id, '.$info[1].', '.$info[2])->from($info[3])->where($info[1].' = ? AND '.$info[2].' = ?')->execute(array($this->getIdentifier(), $identifier));
  677. $result = $stmt->fetch(PDO::FETCH_NUM);
  678. $stmt->closeCursor();
  679. if (!$result) {
  680. MiniMVC_Registry::getInstance()->db->query()->insert(array($info[1], $info[2]), array($this->getIdentifier(), $identifier), $info[3])->execute();
  681. }
  682. }
  683. }
  684. }
  685. /**
  686. *
  687. * @param string $relation the name of a relation
  688. * @param mixed $identifier a related model object, the identifier of a related model or true to unlink all related models
  689. */
  690. public function unlinkRelated($relation, $identifier = true)
  691. {
  692. if (is_array($identifier)) {
  693. foreach ($identifier as $id) {
  694. $this->unlinkRelated($relation, $id);
  695. }
  696. return true;
  697. }
  698. if (!$info = $this->getTable()->getRelation($relation)) {
  699. throw new Exception('Unknown relation "'.$relation.'" for model '.$this->getTable()->getModelName());
  700. }
  701. $tableName = $info[0].'Table';
  702. $table = call_user_func($tableName . '::getInstance');
  703. if (!isset($info[3]) || $info[3] === true) {
  704. if ($info[1] == $this->getTable()->getIdentifier()) {
  705. if (is_object($identifier)) {
  706. $identifier->{$info[2]} = null;
  707. $identifier->save();
  708. if (isset($this->_relations[$relation][$identifier->getIdentifier()])) {
  709. unset($this->_relations[$relation][$identifier->getIdentifier()]);
  710. }
  711. } elseif (isset($this->_relations[$relation][$identifier])) {
  712. $this->_relations[$relation][$identifier]->{$info[2]} = null;
  713. $this->_relations[$relation][$identifier]->save();
  714. unset($this->_relations[$relation][$identifier]);
  715. } elseif($identifier === true) {
  716. if (!$this->getIdentifier()) {
  717. return false;
  718. }
  719. $table->query()->update($info[2])->where($info[2].' = ?')->execute(array(null, $this->getIdentifier()));
  720. //MiniMVC_Registry::getInstance()->db->query()->update($info[0], $info[2])->where($info[2].' = ?')->execute(array(null, $this->getIdentifier()));
  721. } else {
  722. $table->query()->update($info[2])->where($table->getIdentifier().' = ?')->execute(array(null, $identifier));
  723. //MiniMVC_Registry::getInstance()->db->query()->update($info[0], $info[2])->where($table->getIdentifier().' = ?')->execute(array(null, $identifier));
  724. }
  725. } elseif ($info[2] == $table->getIdentifier()) {
  726. if (is_object($identifier)) {
  727. $this->{$info[1]} = null;
  728. if (isset($this->_relations[$relation][$identifier->getIdentifier()])) {
  729. unset($this->_relations[$relation][$identifier->getIdentifier()]);
  730. }
  731. } else {
  732. $this->{$info[1]} = null;
  733. if (isset($this->_relations[$relation][$identifier])) {
  734. unset($this->_relations[$relation][$identifier]);
  735. }
  736. }
  737. $this->save();
  738. }
  739. } else {
  740. if (!$this->getIdentifier()) {
  741. return false;
  742. }
  743. if ($identifier === true) {
  744. MiniMVC_Registry::getInstance()->db->query()->delete($info[3])->where($info[1].' = ?')->execute($this->getIdentifier());
  745. unset($this->_relations[$relation]);
  746. } else {
  747. if (is_object($identifier)) {
  748. if (isset($this->_relations[$relation][$identifier->getIdentifier()])) {
  749. unset($this->_relations[$relation][$identifier->getIdentifier()]);
  750. }
  751. } elseif (isset($this->_relations[$relation][$identifier])) {
  752. unset($this->_relations[$relation][$identifier]);
  753. }
  754. MiniMVC_Registry::getInstance()->db->query()->delete($info[3])->where($info[1].' = ? AND '.$info[2].' = ?')->execute(array($this->getIdentifier(), is_object($identifier) ? $identifier->getIdentifier() : $identifier));
  755. }
  756. }
  757. }
  758. public function save($relations = false)
  759. {
  760. $status = false;
  761. try {
  762. $status = $this->getTable()->save($this);
  763. if ($relations && $status) {
  764. foreach ($this->_relations as $relation => $info) {
  765. $this->saveRelated($relation, true);
  766. }
  767. }
  768. } catch (PDOException $e) {
  769. return false;
  770. }
  771. return $status;
  772. }
  773. public function delete()
  774. {
  775. $status = false;
  776. try {
  777. $status = $this->getTable()->delete($this);
  778. } catch (PDOException $e) {
  779. return false;
  780. }
  781. return $status;
  782. }
  783. public function __toString()
  784. {
  785. return get_class($this).'<pre>'.print_r($this->_properties, true).'</pre>';
  786. }
  787. public function preSave()
  788. {
  789. }
  790. public function preInsert()
  791. {
  792. }
  793. public function preUpdate()
  794. {
  795. }
  796. public function preDelete()
  797. {
  798. }
  799. public function postSave()
  800. {
  801. }
  802. public function postInsert()
  803. {
  804. }
  805. public function postUpdate()
  806. {
  807. }
  808. public function postDelete()
  809. {
  810. }
  811. public function postCreate()
  812. {
  813. }
  814. public function postLoad()
  815. {
  816. }
  817. }