PageRenderTime 57ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/generator/lib/behavior/sortable/SortableBehaviorObjectBuilderModifier.php

https://github.com/Tactics/Propel
PHP | 691 lines | 443 code | 60 blank | 188 comment | 43 complexity | fd76d70d00cfc409c065e65686a5dc31 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Propel package.
  4. * For the full copyright and license information, please view the LICENSE
  5. * file that was distributed with this source code.
  6. *
  7. * @license MIT License
  8. */
  9. /**
  10. * Behavior to add sortable columns and abilities
  11. *
  12. * @author Franรงois Zaninotto
  13. * @author heltem <heltem@o2php.com>
  14. * @package propel.generator.behavior.sortable
  15. */
  16. class SortableBehaviorObjectBuilderModifier
  17. {
  18. protected $behavior, $table, $builder, $objectClassname, $peerClassname;
  19. public function __construct($behavior)
  20. {
  21. $this->behavior = $behavior;
  22. $this->table = $behavior->getTable();
  23. }
  24. protected function getParameter($key)
  25. {
  26. return $this->behavior->getParameter($key);
  27. }
  28. protected function getColumnAttribute($name)
  29. {
  30. return strtolower($this->behavior->getColumnForParameter($name)->getName());
  31. }
  32. protected function getColumnPhpName($name)
  33. {
  34. return $this->behavior->getColumnForParameter($name)->getPhpName();
  35. }
  36. protected function setBuilder($builder)
  37. {
  38. $this->builder = $builder;
  39. $this->objectClassname = $builder->getStubObjectBuilder()->getClassname();
  40. $this->queryClassname = $builder->getStubQueryBuilder()->getClassname();
  41. $this->peerClassname = $builder->getStubPeerBuilder()->getClassname();
  42. }
  43. /**
  44. * Get the getter of the column of the behavior
  45. *
  46. * @return string The related getter, e.g. 'getRank'
  47. */
  48. protected function getColumnGetter($columnName = 'rank_column')
  49. {
  50. return 'get' . $this->behavior->getColumnForParameter($columnName)->getPhpName();
  51. }
  52. /**
  53. * Get the setter of the column of the behavior
  54. *
  55. * @return string The related setter, e.g. 'setRank'
  56. */
  57. protected function getColumnSetter($columnName = 'rank_column')
  58. {
  59. return 'set' . $this->behavior->getColumnForParameter($columnName)->getPhpName();
  60. }
  61. public function preSave($builder)
  62. {
  63. return "\$this->processSortableQueries(\$con);";
  64. }
  65. public function preInsert($builder)
  66. {
  67. $useScope = $this->behavior->useScope();
  68. $this->setBuilder($builder);
  69. return "if (!\$this->isColumnModified({$this->peerClassname}::RANK_COL)) {
  70. \$this->{$this->getColumnSetter()}({$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con) + 1);
  71. }
  72. ";
  73. }
  74. public function preUpdate($builder)
  75. {
  76. if ($this->behavior->useScope()) {
  77. $this->setBuilder($builder);
  78. return "// if scope has changed and rank was not modified (if yes, assuming superior action)
  79. // insert object to the end of new scope and cleanup old one
  80. if (\$this->isColumnModified({$this->peerClassname}::SCOPE_COL) && !\$this->isColumnModified({$this->peerClassname}::RANK_COL)) {
  81. {$this->peerClassname}::shiftRank(-1, \$this->{$this->getColumnGetter()}() + 1, null, \$this->oldScope, \$con);
  82. \$this->insertAtBottom(\$con);
  83. }
  84. ";
  85. }
  86. }
  87. public function preDelete($builder)
  88. {
  89. $useScope = $this->behavior->useScope();
  90. $this->setBuilder($builder);
  91. return "
  92. {$this->peerClassname}::shiftRank(-1, \$this->{$this->getColumnGetter()}() + 1, null, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
  93. {$this->peerClassname}::clearInstancePool();
  94. ";
  95. }
  96. public function objectAttributes($builder)
  97. {
  98. $script = "
  99. /**
  100. * Queries to be executed in the save transaction
  101. * @var array
  102. */
  103. protected \$sortableQueries = array();
  104. ";
  105. if ($this->behavior->useScope()) {
  106. $script .= "
  107. /**
  108. * The old scope value.
  109. * @var int
  110. */
  111. protected \$oldScope;
  112. ";
  113. }
  114. return $script;
  115. }
  116. public function objectMethods($builder)
  117. {
  118. $this->setBuilder($builder);
  119. $script = '';
  120. if ($this->getParameter('rank_column') != 'rank') {
  121. $this->addRankAccessors($script);
  122. }
  123. if ($this->behavior->useScope() &&
  124. $this->getParameter('scope_column') != 'scope_value') {
  125. $this->addScopeAccessors($script);
  126. }
  127. $this->addIsFirst($script);
  128. $this->addIsLast($script);
  129. $this->addGetNext($script);
  130. $this->addGetPrevious($script);
  131. $this->addInsertAtRank($script);
  132. $this->addInsertAtBottom($script);
  133. $this->addInsertAtTop($script);
  134. $this->addMoveToRank($script);
  135. $this->addSwapWith($script);
  136. $this->addMoveUp($script);
  137. $this->addMoveDown($script);
  138. $this->addMoveToTop($script);
  139. $this->addMoveToBottom($script);
  140. $this->addRemoveFromList($script);
  141. $this->addProcessSortableQueries($script);
  142. return $script;
  143. }
  144. public function objectFilter(&$script, $builder)
  145. {
  146. if ($this->behavior->useScope()) {
  147. $methodName = $this->getColumnSetter('scope_column');
  148. $search = "if (\$this->{$this->getColumnAttribute('scope_column')} !== \$v) {";
  149. $replace = $search . "
  150. // sortable behavior
  151. \$this->oldScope = \$this->{$this->getColumnGetter('scope_column')}();
  152. ";
  153. $script = str_replace($search, $replace, $script);
  154. }
  155. }
  156. /**
  157. * Get the wraps for getter/setter, if the rank column has not the default name
  158. *
  159. * @return string
  160. */
  161. protected function addRankAccessors(&$script)
  162. {
  163. $script .= "
  164. /**
  165. * Wrap the getter for rank value
  166. *
  167. * @return int
  168. */
  169. public function getRank()
  170. {
  171. return \$this->{$this->getColumnAttribute('rank_column')};
  172. }
  173. /**
  174. * Wrap the setter for rank value
  175. *
  176. * @param int
  177. * @return {$this->objectClassname}
  178. */
  179. public function setRank(\$v)
  180. {
  181. return \$this->{$this->getColumnSetter()}(\$v);
  182. }
  183. ";
  184. }
  185. /**
  186. * Get the wraps for getter/setter, if the scope column has not the default name
  187. *
  188. * @return string
  189. */
  190. protected function addScopeAccessors(&$script)
  191. {
  192. $script .= "
  193. /**
  194. * Wrap the getter for scope value
  195. *
  196. * @return int
  197. */
  198. public function getScopeValue()
  199. {
  200. return \$this->{$this->getColumnAttribute('scope_column')};
  201. }
  202. /**
  203. * Wrap the setter for scope value
  204. *
  205. * @param int
  206. * @return {$this->objectClassname}
  207. */
  208. public function setScopeValue(\$v)
  209. {
  210. return \$this->{$this->getColumnSetter('scope_column')}(\$v);
  211. }
  212. ";
  213. }
  214. protected function addIsFirst(&$script)
  215. {
  216. $script .= "
  217. /**
  218. * Check if the object is first in the list, i.e. if it has 1 for rank
  219. *
  220. * @return boolean
  221. */
  222. public function isFirst()
  223. {
  224. return \$this->{$this->getColumnGetter()}() == 1;
  225. }
  226. ";
  227. }
  228. protected function addIsLast(&$script)
  229. {
  230. $useScope = $this->behavior->useScope();
  231. $script .= "
  232. /**
  233. * Check if the object is last in the list, i.e. if its rank is the highest rank
  234. *
  235. * @param PropelPDO \$con optional connection
  236. *
  237. * @return boolean
  238. */
  239. public function isLast(PropelPDO \$con = null)
  240. {
  241. return \$this->{$this->getColumnGetter()}() == {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
  242. }
  243. ";
  244. }
  245. protected function addGetNext(&$script)
  246. {
  247. $useScope = $this->behavior->useScope();
  248. $script .= "
  249. /**
  250. * Get the next item in the list, i.e. the one for which rank is immediately higher
  251. *
  252. * @param PropelPDO \$con optional connection
  253. *
  254. * @return {$this->objectClassname}
  255. */
  256. public function getNext(PropelPDO \$con = null)
  257. {";
  258. if ($this->behavior->getParameter('rank_column') == 'rank' && $useScope) {
  259. $script .= "
  260. return {$this->queryClassname}::create()
  261. ->filterByRank(\$this->{$this->getColumnGetter()}() + 1)
  262. ->inList(\$this->{$this->getColumnGetter('scope_column')}())
  263. ->findOne(\$con);";
  264. } else {
  265. $script .= "
  266. return {$this->queryClassname}::create()->findOneByRank(\$this->{$this->getColumnGetter()}() + 1, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);";
  267. }
  268. $script .= "
  269. }
  270. ";
  271. }
  272. protected function addGetPrevious(&$script)
  273. {
  274. $useScope = $this->behavior->useScope();
  275. $script .= "
  276. /**
  277. * Get the previous item in the list, i.e. the one for which rank is immediately lower
  278. *
  279. * @param PropelPDO \$con optional connection
  280. *
  281. * @return {$this->objectClassname}
  282. */
  283. public function getPrevious(PropelPDO \$con = null)
  284. {";
  285. if ($this->behavior->getParameter('rank_column') == 'rank' && $useScope) {
  286. $script .= "
  287. return {$this->queryClassname}::create()
  288. ->filterByRank(\$this->{$this->getColumnGetter()}() - 1)
  289. ->inList(\$this->{$this->getColumnGetter('scope_column')}())
  290. ->findOne(\$con);";
  291. } else {
  292. $script .= "
  293. return {$this->queryClassname}::create()->findOneByRank(\$this->{$this->getColumnGetter()}() - 1, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);";
  294. }
  295. $script .= "
  296. }
  297. ";
  298. }
  299. protected function addInsertAtRank(&$script)
  300. {
  301. $useScope = $this->behavior->useScope();
  302. $script .= "
  303. /**
  304. * Insert at specified rank
  305. * The modifications are not persisted until the object is saved.
  306. *
  307. * @param integer \$rank rank value
  308. * @param PropelPDO \$con optional connection
  309. *
  310. * @return {$this->objectClassname} the current object
  311. *
  312. * @throws PropelException
  313. */
  314. public function insertAtRank(\$rank, PropelPDO \$con = null)
  315. {";
  316. $script .= "
  317. \$maxRank = {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
  318. if (\$rank < 1 || \$rank > \$maxRank + 1) {
  319. throw new PropelException('Invalid rank ' . \$rank);
  320. }
  321. // move the object in the list, at the given rank
  322. \$this->{$this->getColumnSetter()}(\$rank);
  323. if (\$rank != \$maxRank + 1) {
  324. // Keep the list modification query for the save() transaction
  325. \$this->sortableQueries []= array(
  326. 'callable' => array(self::PEER, 'shiftRank'),
  327. 'arguments' => array(1, \$rank, null, " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}()" : '') . ")
  328. );
  329. }
  330. return \$this;
  331. }
  332. ";
  333. }
  334. protected function addInsertAtBottom(&$script)
  335. {
  336. $useScope = $this->behavior->useScope();
  337. $script .= "
  338. /**
  339. * Insert in the last rank
  340. * The modifications are not persisted until the object is saved.
  341. *
  342. * @param PropelPDO \$con optional connection
  343. *
  344. * @return {$this->objectClassname} the current object
  345. *
  346. * @throws PropelException
  347. */
  348. public function insertAtBottom(PropelPDO \$con = null)
  349. {";
  350. $script .= "
  351. \$this->{$this->getColumnSetter()}({$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con) + 1);
  352. return \$this;
  353. }
  354. ";
  355. }
  356. protected function addInsertAtTop(&$script)
  357. {
  358. $script .= "
  359. /**
  360. * Insert in the first rank
  361. * The modifications are not persisted until the object is saved.
  362. *
  363. * @return {$this->objectClassname} the current object
  364. */
  365. public function insertAtTop()
  366. {
  367. return \$this->insertAtRank(1);
  368. }
  369. ";
  370. }
  371. protected function addMoveToRank(&$script)
  372. {
  373. $useScope = $this->behavior->useScope();
  374. $peerClassname = $this->peerClassname;
  375. $script .= "
  376. /**
  377. * Move the object to a new rank, and shifts the rank
  378. * Of the objects inbetween the old and new rank accordingly
  379. *
  380. * @param integer \$newRank rank value
  381. * @param PropelPDO \$con optional connection
  382. *
  383. * @return {$this->objectClassname} the current object
  384. *
  385. * @throws PropelException
  386. */
  387. public function moveToRank(\$newRank, PropelPDO \$con = null)
  388. {
  389. if (\$this->isNew()) {
  390. throw new PropelException('New objects cannot be moved. Please use insertAtRank() instead');
  391. }
  392. if (\$con === null) {
  393. \$con = Propel::getConnection($peerClassname::DATABASE_NAME);
  394. }
  395. if (\$newRank < 1 || \$newRank > {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con)) {
  396. throw new PropelException('Invalid rank ' . \$newRank);
  397. }
  398. \$oldRank = \$this->{$this->getColumnGetter()}();
  399. if (\$oldRank == \$newRank) {
  400. return \$this;
  401. }
  402. \$con->beginTransaction();
  403. try {
  404. // shift the objects between the old and the new rank
  405. \$delta = (\$oldRank < \$newRank) ? -1 : 1;
  406. $peerClassname::shiftRank(\$delta, min(\$oldRank, \$newRank), max(\$oldRank, \$newRank), " . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
  407. // move the object to its new rank
  408. \$this->{$this->getColumnSetter()}(\$newRank);
  409. \$this->save(\$con);
  410. \$con->commit();
  411. return \$this;
  412. } catch (Exception \$e) {
  413. \$con->rollback();
  414. throw \$e;
  415. }
  416. }
  417. ";
  418. }
  419. protected function addSwapWith(&$script)
  420. {
  421. $script .= "
  422. /**
  423. * Exchange the rank of the object with the one passed as argument, and saves both objects
  424. *
  425. * @param {$this->objectClassname} \$object
  426. * @param PropelPDO \$con optional connection
  427. *
  428. * @return {$this->objectClassname} the current object
  429. *
  430. * @throws Exception if the database cannot execute the two updates
  431. */
  432. public function swapWith(\$object, PropelPDO \$con = null)
  433. {
  434. if (\$con === null) {
  435. \$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
  436. }
  437. \$con->beginTransaction();
  438. try {";
  439. if ($this->behavior->useScope()) {
  440. $script .= "
  441. \$oldScope = \$this->{$this->getColumnGetter('scope_column')}();
  442. \$newScope = \$object->{$this->getColumnGetter('scope_column')}();
  443. if (\$oldScope != \$newScope) {
  444. \$this->{$this->getColumnSetter('scope_column')}(\$newScope);
  445. \$object->{$this->getColumnSetter('scope_column')}(\$oldScope);
  446. }";
  447. }
  448. $script .= "
  449. \$oldRank = \$this->{$this->getColumnGetter()}();
  450. \$newRank = \$object->{$this->getColumnGetter()}();
  451. \$this->{$this->getColumnSetter()}(\$newRank);
  452. \$this->save(\$con);
  453. \$object->{$this->getColumnSetter()}(\$oldRank);
  454. \$object->save(\$con);
  455. \$con->commit();
  456. return \$this;
  457. } catch (Exception \$e) {
  458. \$con->rollback();
  459. throw \$e;
  460. }
  461. }
  462. ";
  463. }
  464. protected function addMoveUp(&$script)
  465. {
  466. $script .= "
  467. /**
  468. * Move the object higher in the list, i.e. exchanges its rank with the one of the previous object
  469. *
  470. * @param PropelPDO \$con optional connection
  471. *
  472. * @return {$this->objectClassname} the current object
  473. */
  474. public function moveUp(PropelPDO \$con = null)
  475. {
  476. if (\$this->isFirst()) {
  477. return \$this;
  478. }
  479. if (\$con === null) {
  480. \$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
  481. }
  482. \$con->beginTransaction();
  483. try {
  484. \$prev = \$this->getPrevious(\$con);
  485. \$this->swapWith(\$prev, \$con);
  486. \$con->commit();
  487. return \$this;
  488. } catch (Exception \$e) {
  489. \$con->rollback();
  490. throw \$e;
  491. }
  492. }
  493. ";
  494. }
  495. protected function addMoveDown(&$script)
  496. {
  497. $script .= "
  498. /**
  499. * Move the object higher in the list, i.e. exchanges its rank with the one of the next object
  500. *
  501. * @param PropelPDO \$con optional connection
  502. *
  503. * @return {$this->objectClassname} the current object
  504. */
  505. public function moveDown(PropelPDO \$con = null)
  506. {
  507. if (\$this->isLast(\$con)) {
  508. return \$this;
  509. }
  510. if (\$con === null) {
  511. \$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
  512. }
  513. \$con->beginTransaction();
  514. try {
  515. \$next = \$this->getNext(\$con);
  516. \$this->swapWith(\$next, \$con);
  517. \$con->commit();
  518. return \$this;
  519. } catch (Exception \$e) {
  520. \$con->rollback();
  521. throw \$e;
  522. }
  523. }
  524. ";
  525. }
  526. protected function addMoveToTop(&$script)
  527. {
  528. $script .= "
  529. /**
  530. * Move the object to the top of the list
  531. *
  532. * @param PropelPDO \$con optional connection
  533. *
  534. * @return {$this->objectClassname} the current object
  535. */
  536. public function moveToTop(PropelPDO \$con = null)
  537. {
  538. if (\$this->isFirst()) {
  539. return \$this;
  540. }
  541. return \$this->moveToRank(1, \$con);
  542. }
  543. ";
  544. }
  545. protected function addMoveToBottom(&$script)
  546. {
  547. $useScope = $this->behavior->useScope();
  548. $script .= "
  549. /**
  550. * Move the object to the bottom of the list
  551. *
  552. * @param PropelPDO \$con optional connection
  553. *
  554. * @return integer the old object's rank
  555. */
  556. public function moveToBottom(PropelPDO \$con = null)
  557. {
  558. if (\$this->isLast(\$con)) {
  559. return false;
  560. }
  561. if (\$con === null) {
  562. \$con = Propel::getConnection({$this->peerClassname}::DATABASE_NAME);
  563. }
  564. \$con->beginTransaction();
  565. try {
  566. \$bottom = {$this->queryClassname}::create()->getMaxRank(" . ($useScope ? "\$this->{$this->getColumnGetter('scope_column')}(), " : '') . "\$con);
  567. \$res = \$this->moveToRank(\$bottom, \$con);
  568. \$con->commit();
  569. return \$res;
  570. } catch (Exception \$e) {
  571. \$con->rollback();
  572. throw \$e;
  573. }
  574. }
  575. ";
  576. }
  577. protected function addRemoveFromList(&$script)
  578. {
  579. $useScope = $this->behavior->useScope();
  580. $script .= "
  581. /**
  582. * Removes the current object from the list".($useScope ? ' (moves it to the null scope)' : '').".
  583. * The modifications are not persisted until the object is saved.
  584. *
  585. * @param PropelPDO \$con optional connection
  586. *
  587. * @return {$this->objectClassname} the current object
  588. */
  589. public function removeFromList(PropelPDO \$con = null)
  590. {";
  591. if ($useScope) {
  592. $script .= "
  593. // check if object is already removed
  594. if (\$this->{$this->getColumnGetter('scope_column')}() === null) {
  595. throw new PropelException('Object is already removed (has null scope)');
  596. }
  597. // move the object to the end of null scope
  598. \$this->{$this->getColumnSetter('scope_column')}(null);
  599. // \$this->insertAtBottom(\$con);";
  600. } else {
  601. $script .= "
  602. // Keep the list modification query for the save() transaction
  603. \$this->sortableQueries []= array(
  604. 'callable' => array(self::PEER, 'shiftRank'),
  605. 'arguments' => array(-1, \$this->{$this->getColumnGetter()}() + 1, null" . ($useScope ? ", \$this->{$this->getColumnGetter('scope_column')}()" : '') . ")
  606. );
  607. // remove the object from the list
  608. \$this->{$this->getColumnSetter('rank_column')}(null);";
  609. }
  610. $script .= "
  611. return \$this;
  612. }
  613. ";
  614. }
  615. protected function addProcessSortableQueries(&$script)
  616. {
  617. $script .= "
  618. /**
  619. * Execute queries that were saved to be run inside the save transaction
  620. */
  621. protected function processSortableQueries(\$con)
  622. {
  623. foreach (\$this->sortableQueries as \$query) {
  624. \$query['arguments'][]= \$con;
  625. call_user_func_array(\$query['callable'], \$query['arguments']);
  626. }
  627. \$this->sortableQueries = array();
  628. }
  629. ";
  630. }
  631. }