PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Persistent.php

http://shozu.googlecode.com/
PHP | 489 lines | 344 code | 19 blank | 126 comment | 42 complexity | 683deca957101d4230df7624d42cfdee MD5 | raw file
  1. <?php
  2. /**
  3. * THIS CLASS IS DEPRECATED !
  4. * Do not use in new projects.
  5. */
  6. namespace shozu;
  7. //trigger_error('"Persistent" is deprecated: use ActiveBean and RedBean.', E_USER_DEPRECATED);
  8. use \shozu\Record as Record;
  9. use \shozu\Relation as Relation;
  10. /**
  11. * Further extend Record to handle relations, uids, etc.
  12. *
  13. * @package MVC
  14. */
  15. abstract class Persistent extends Record
  16. {
  17. private $isNew = true;
  18. private $linkop = array();
  19. /**
  20. * Persistent object
  21. *
  22. * @param array array as key=>value pairs
  23. */
  24. public function __construct(array $values = null)
  25. {
  26. // uid is returned by the PHP function uniqid(null, true)
  27. $this->addColumn(array(
  28. 'name' => 'uid',
  29. 'type' => 'string',
  30. 'length' => 22,
  31. 'primary' => true
  32. ));
  33. $this->addColumn(array(
  34. 'name' => 'modified_at',
  35. 'type' => 'datetime'
  36. ));
  37. $this->addColumn(array(
  38. 'name' => 'created_at',
  39. 'type' => 'datetime'
  40. ));
  41. parent::__construct($values);
  42. if(is_null($this->uid))
  43. {
  44. $this->uid = self::uidgen();
  45. }
  46. else
  47. {
  48. // if uid is available, we *assume* object is not new and is clean
  49. $this->isNew = false;
  50. $this->isDirty = false;
  51. }
  52. }
  53. /**
  54. * Save object to database
  55. *
  56. * @param boolean $force force save even if instance hasn't changed
  57. */
  58. public function save($force = false)
  59. {
  60. if(method_exists($this, 'preSave'))
  61. {
  62. $this->preSave();
  63. }
  64. // stamp object
  65. $now = time();
  66. if(is_null($this->created_at))
  67. {
  68. $this->created_at = $now;
  69. }
  70. $this->modified_at = $now;
  71. try
  72. {
  73. if($this->_validates())
  74. {
  75. $this->_save($force);
  76. }
  77. else
  78. {
  79. throw new \Exception('Record validation error. ' . $this->lastError);
  80. }
  81. }
  82. catch(\PDOException $e)
  83. {
  84. // table not found, try to create it
  85. if($e->getCode() == self::SQL_UNKNOWN_TABLE)
  86. {
  87. $this->createTable();
  88. $this->_save($force);
  89. }
  90. else
  91. {
  92. throw $e;
  93. }
  94. }
  95. if(method_exists($this, 'postSave'))
  96. {
  97. $this->postSave();
  98. }
  99. }
  100. private function _save($force = false)
  101. {
  102. try
  103. {
  104. self::getDB()->beginTransaction();
  105. $oldIsDirty = $this->isDirty;
  106. $oldIsNew = $this->isNew;
  107. if($force)
  108. {
  109. $this->isDirty = true;
  110. }
  111. if($this->isDirty)
  112. {
  113. if($this->isNew)
  114. {
  115. $this->insert();
  116. $this->isNew = false;
  117. }
  118. else
  119. {
  120. $this->update();
  121. }
  122. $this->isDirty = false;
  123. }
  124. foreach($this->linkop as $op)
  125. {
  126. if($op[0] == 'link')
  127. {
  128. Relation::link($this, $op[1]);
  129. }
  130. if($op[0] == 'unlink')
  131. {
  132. Relation::unlink($this, $op[1]);
  133. }
  134. }
  135. self::getDB()->commit();
  136. }
  137. catch(\Exception $e)
  138. {
  139. self::getDB()->rollBack();
  140. $this->isDirty = $oldIsDirty;
  141. $this->isNew = $oldIsNew;
  142. throw $e;
  143. }
  144. }
  145. /**
  146. * Link list of objects. Objects must inherit from Persistent.
  147. *
  148. * <code>
  149. * $book->link($author1, $author2, $editor, $tag1, $tag2);
  150. * </code>
  151. *
  152. * @param Persistent
  153. */
  154. public function link()
  155. {
  156. foreach(func_get_args() as $object)
  157. {
  158. $this->linkop[] = array('link', $object);
  159. }
  160. }
  161. /**
  162. * Unlink objects. Objects must inherit from Persistent.
  163. *
  164. * <code>
  165. * // unlink DOES NOT delete objects from their own table.
  166. * $book->unlink($author1, $author2, $editor, $tag1, $tag2);
  167. * // unlink all instances of class
  168. * $book->unlink('Tag');
  169. * </code>
  170. *
  171. * @param Persistent
  172. */
  173. public function unlink()
  174. {
  175. if(func_num_args() === 1 && is_string(func_get_arg(0)))
  176. {
  177. $class = func_get_arg(0);
  178. $instances = $this->getRelated($class);
  179. foreach($instances as $instance)
  180. {
  181. $this->linkop[] = array('unlink', $instance);
  182. }
  183. }
  184. else
  185. {
  186. foreach(func_get_args() as $object)
  187. {
  188. $this->linkop[] = array('unlink', $object);
  189. }
  190. }
  191. }
  192. /**
  193. * Get related object by class.
  194. *
  195. * <code>
  196. * // fetch book authors whose name is "london"
  197. * $authors = $book->getRelated('Author',
  198. * 'Author.name like :name order by Author.year',
  199. * array(':name' => 'london'));
  200. * </code>
  201. *
  202. * @param string $class Class name.
  203. * @param string $conditions additionnal filters
  204. * @param array $replace strings to quote
  205. */
  206. public function getRelated($class, $conditions = '', array $replace = array())
  207. {
  208. if(substr($class,0,1) != '\\')
  209. {
  210. $class = '\\' . $class;
  211. }
  212. $db = self::getDB();
  213. if(!empty($conditions))
  214. {
  215. if(!is_null($replace))
  216. {
  217. foreach($replace as $key => $val)
  218. {
  219. $replace[$key] = $db->quote($val);
  220. }
  221. }
  222. $conditions = str_replace($class . '.', 'a.', $conditions);
  223. $conditions = ' and ' . str_replace(array_keys($replace), array_values($replace), $conditions);
  224. }
  225. $classes = array(Relation::getClass($this), $class);
  226. sort($classes);
  227. $relationType = $classes[0] . '-' . $classes[1];
  228. if($classes[0] == Relation::getClass($this))
  229. {
  230. $uid_local = 'uid_a';
  231. $uid_foreign = 'uid_b';
  232. }
  233. else
  234. {
  235. $uid_local = 'uid_b';
  236. $uid_foreign = 'uid_a';
  237. }
  238. $sql = 'a.* FROM ' . Relation::getTableName() . ' r inner join ' . \shozu\Inflector::model2dbName($class) . ' a on a.uid = r.:UID_FOREIGN: where :UID_LOCAL:=' . $db->quote($this->uid) . ' and r.relation_parties='. $db->quote($relationType) . $conditions;
  239. try
  240. {
  241. $rows = $db->fetchAll(str_replace(array(':UID_FOREIGN:',':UID_LOCAL:'),
  242. array($uid_foreign, $uid_local),
  243. $sql));
  244. }
  245. catch(\PDOException $e)
  246. {
  247. if($e->getCode() == self::SQL_UNKNOWN_TABLE)
  248. {
  249. return array();
  250. }
  251. }
  252. // additionnal query for Xyz-Xyz relations
  253. // TODO: find a better way ! This will not give expected results if used
  254. // with an order by clause
  255. if($classes[0] == $classes[1])
  256. {
  257. // swap uids
  258. list($uid_local, $uid_foreign) = array($uid_foreign, $uid_local);
  259. // re-run query, merge results
  260. $rows = array_merge($rows,$db->fetchAll(str_replace(array(':UID_FOREIGN:',':UID_LOCAL:'),
  261. array($uid_foreign, $uid_local),
  262. $sql)));
  263. }
  264. if(count($rows) > 0)
  265. {
  266. return Record::hydrate($class, $rows);
  267. }
  268. return array();
  269. }
  270. /**
  271. * Delete instance from database, removing all relations.
  272. */
  273. public function delete()
  274. {
  275. $db = self::getDB();
  276. try
  277. {
  278. $db->beginTransaction();
  279. Relation::remove($this);
  280. $db->exec('delete from ' . \shozu\Inflector::model2dbName(Relation::getClass($this)) . ' where uid=' . $db->quote($this->uid));
  281. $db->commit();
  282. }
  283. catch(\Exception $e)
  284. {
  285. $db->rollBack();
  286. throw $e;
  287. }
  288. }
  289. /**
  290. * Override Record hydrate
  291. *
  292. * <code>
  293. * $myClassInstances = myClass::hydrate('firstname=? and lastname=?',array('john', 'doe'));
  294. * </code>
  295. *
  296. * @param string sql query conditions
  297. * @param array to be replaced
  298. * @return array
  299. */
  300. public static function hydrate($query = '', array $replace = null, $return_hydrator = false)
  301. {
  302. $class = get_called_class();
  303. $query = '* from ' . self::getTableName($class) . (!empty($query) ? ' where ' . $query : '');
  304. if($return_hydrator)
  305. {
  306. return new \shozu\ActiveBean\Hydrator(self::getDB()->getAdapter(), $class, 'select '.$query, $replace);
  307. }
  308. try
  309. {
  310. return parent::hydrate($class, self::getDB()->fetchAll($query, $replace));
  311. }
  312. catch(\PDOException $e)
  313. {
  314. // table not found
  315. if($e->getCode() == self::SQL_UNKNOWN_TABLE)
  316. {
  317. // do nothing, will be created with first save
  318. return array();
  319. }
  320. else
  321. {
  322. throw $e;
  323. }
  324. }
  325. }
  326. /**
  327. * Hydrate only one object
  328. *
  329. * <code>
  330. * $mySingleClassInstance = myClass::hydrateOne('firstname=? and lastname=?',array('john', 'doe'));
  331. * </code>
  332. *
  333. * @param string sql query conditions
  334. * @param array to be replaced
  335. * @return Persistent
  336. */
  337. public static function hydrateOne($query, array $replace = null)
  338. {
  339. $class = get_called_class();
  340. $query = '* from ' . self::getTableName($class) . ' where ' . $query . ' limit 1';
  341. try
  342. {
  343. $objects = parent::hydrate($class, self::getDB()->fetchAll($query, $replace));
  344. if(count($objects) > 0)
  345. {
  346. return $objects[0];
  347. }
  348. return false;
  349. }
  350. catch(\PDOException $e)
  351. {
  352. // table not found
  353. if($e->getCode() == self::SQL_UNKNOWN_TABLE)
  354. {
  355. // do nothing, will be created with first save
  356. return false;
  357. }
  358. else
  359. {
  360. throw $e;
  361. }
  362. }
  363. }
  364. /**
  365. * Hydrate instances from given uids
  366. *
  367. * @param array $uids
  368. * @return array
  369. */
  370. public static function hydrateFromUids(array $uids)
  371. {
  372. if(count($uids) == 0)
  373. {
  374. return array();
  375. }
  376. $db = self::getDB();
  377. $class = get_called_class();
  378. foreach($uids as $key => $uid)
  379. {
  380. $uids[$key] = $db->quote($uid);
  381. }
  382. $query = '* FROM ' . self::getTableName($class) . ' WHERE uid IN (' . implode(',', $uids) . ') ORDER BY FIELD(uid,'. implode(',', $uids) .')';
  383. try
  384. {
  385. return parent::hydrate($class, $db->fetchAll($query));
  386. }
  387. catch(\PDOException $e)
  388. {
  389. // table not found
  390. if($e->getCode() == self::SQL_UNKNOWN_TABLE)
  391. {
  392. // do nothing, will be created with first save
  393. return array();
  394. }
  395. else
  396. {
  397. throw $e;
  398. }
  399. }
  400. }
  401. /**
  402. * Hydrate one instance from uid
  403. *
  404. * @param string $uid
  405. * @return mixed
  406. */
  407. public static function hydrateOneFromUid($uid)
  408. {
  409. $res = self::hydrateFromUids(array($uid));
  410. if(count($res) == 0)
  411. {
  412. return false;
  413. }
  414. return $res[0];
  415. }
  416. /**
  417. * hydrateOneFromUid alias
  418. *
  419. * @param string $uid
  420. * @return mixed
  421. */
  422. public static function findOneByUid($uid)
  423. {
  424. return self::hydrateOneFromUid($uid);
  425. }
  426. /**
  427. * Count all instances in database
  428. *
  429. * @return integer
  430. */
  431. public static function countAll()
  432. {
  433. return self::count();
  434. }
  435. /**
  436. * Count all instances in database with given where clause
  437. *
  438. * @param string $where
  439. * @return integer
  440. */
  441. public static function count($where = '1', array $replace = null)
  442. {
  443. try
  444. {
  445. $res = self::getDB()->fetchOne('COUNT(*) as total FROM ' . self::getTableName(get_called_class()) . ' WHERE ' . $where, $replace);
  446. }
  447. catch(\PDOException $e)
  448. {
  449. // table not found
  450. if($e->getCode() == self::SQL_UNKNOWN_TABLE)
  451. {
  452. // do nothing, will be created with first save
  453. return 0;
  454. }
  455. else
  456. {
  457. throw $e;
  458. }
  459. }
  460. if($res)
  461. {
  462. return (int)$res;
  463. }
  464. return 0;
  465. }
  466. public static function uidgen()
  467. {
  468. return str_replace('.', '', uniqid(null, true));
  469. }
  470. }