PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/template/Sortable.php

https://github.com/robo47/csDoctrineActAsSortablePlugin
PHP | 376 lines | 211 code | 63 blank | 102 comment | 18 complexity | 388c5b32d6d697c9b700830ca6c2b61c MD5 | raw file
  1. <?php
  2. /**
  3. * Easily create a slug for each record based on a specified set of fields
  4. *
  5. * @package csDoctrineSortablePlugin
  6. * @subpackage template
  7. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  8. * @link www.phpdoctrine.org
  9. * @since 1.0
  10. * @version $Revision$
  11. * @author Travis Black <tblack@centresource.com>
  12. */
  13. class Doctrine_Template_Sortable extends Doctrine_Template
  14. {
  15. /**
  16. * Array of Sortable options
  17. *
  18. * @var string
  19. */
  20. protected $_options = array('name' => 'position',
  21. 'alias' => null,
  22. 'type' => 'integer',
  23. 'length' => 8,
  24. 'unique' => true,
  25. 'options' => array(),
  26. 'fields' => array(),
  27. 'uniqueBy' => array(),
  28. 'uniqueIndex' => true,
  29. 'indexName' => 'sortable'
  30. );
  31. /**
  32. * __construct
  33. *
  34. * @param string $array
  35. * @return void
  36. */
  37. public function __construct(array $options = array())
  38. {
  39. $this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options);
  40. }
  41. /**
  42. * Set table definition for sortable behavior
  43. * (borrowed and modified from Sluggable in Doctrine core)
  44. *
  45. * @return void
  46. */
  47. public function setTableDefinition()
  48. {
  49. $name = $this->_options['name'];
  50. if ($this->_options['alias'])
  51. {
  52. $name .= ' as ' . $this->_options['alias'];
  53. }
  54. $this->hasColumn($name, $this->_options['type'], $this->_options['length'], $this->_options['options']);
  55. if (!empty($this->_options['uniqueBy']) && !is_array($this->_options['uniqueBy']))
  56. {
  57. throw new sfException("Sortable option 'uniqueBy' must be an array");
  58. }
  59. if ($this->_options['uniqueIndex'] == true && ! empty($this->_options['uniqueBy']))
  60. {
  61. $indexFields = array($this->_options['name']);
  62. $indexFields = array_merge($indexFields, $this->_options['uniqueBy']);
  63. $this->index($this->getSortableIndexName(), array('fields' => $indexFields, 'type' => 'unique'));
  64. }
  65. elseif ($this->_options['unique'])
  66. {
  67. $indexFields = array($this->_options['name']);
  68. $this->index($this->getSortableIndexName(), array('fields' => $indexFields, 'type' => 'unique'));
  69. }
  70. $this->addListener(new Doctrine_Template_Listener_Sortable($this->_options));
  71. }
  72. /**
  73. * Returns the name of the index to create for the position field.
  74. *
  75. * @return string
  76. */
  77. protected function getSortableIndexName()
  78. {
  79. return sprintf('%s_%s_%s', $this->getTable()->getTableName(), $this->_options['name'], $this->_options['indexName']);
  80. }
  81. /**
  82. * Demotes a sortable object to a lower position
  83. *
  84. * @return void
  85. */
  86. public function demote()
  87. {
  88. $object = $this->getInvoker();
  89. $position = $object->get($this->_options['name']);
  90. if ($position < $object->getFinalPosition())
  91. {
  92. $object->moveToPosition($position + 1);
  93. }
  94. }
  95. /**
  96. * Promotes a sortable object to a higher position
  97. *
  98. * @return void
  99. */
  100. public function promote()
  101. {
  102. $object = $this->getInvoker();
  103. $position = $object->get($this->_options['name']);
  104. if ($position > 1)
  105. {
  106. $object->moveToPosition($position - 1);
  107. }
  108. }
  109. /**
  110. * Sets a sortable object to the first position
  111. *
  112. * @return void
  113. */
  114. public function moveToFirst()
  115. {
  116. $object = $this->getInvoker();
  117. $object->moveToPosition(1);
  118. }
  119. /**
  120. * Sets a sortable object to the last position
  121. *
  122. * @return void
  123. */
  124. public function moveToLast()
  125. {
  126. $object = $this->getInvoker();
  127. $object->moveToPosition($object->getFinalPosition());
  128. }
  129. /**
  130. * Moves a sortable object to a designate position
  131. *
  132. * @param int $newPosition
  133. * @return void
  134. */
  135. public function moveToPosition($newPosition)
  136. {
  137. if (!is_int($newPosition))
  138. {
  139. throw new Doctrine_Exception('moveToPosition requires an Integer as the new position. Entered ' . $newPosition);
  140. }
  141. $object = $this->getInvoker();
  142. $position = $object->get($this->_options['name']);
  143. $connection = $object->getTable()->getConnection();
  144. //begin Transaction
  145. $connection->beginTransaction();
  146. // Position is required to be unique. Blanks it out before it moves others up/down.
  147. $object->set($this->_options['name'], null);
  148. $object->save();
  149. if ($position > $newPosition)
  150. {
  151. $q = $object->getTable()->createQuery()
  152. ->update(get_class($object))
  153. ->set($this->_options['name'], $this->_options['name'] . ' + 1')
  154. ->where($this->_options['name'] . ' < ?', $position)
  155. ->andWhere($this->_options['name'] . ' >= ?', $newPosition)
  156. ->orderBy($this->_options['name'] . ' DESC');
  157. foreach ($this->_options['uniqueBy'] as $field)
  158. {
  159. $q->addWhere($field . ' = ?', $object[$field]);
  160. }
  161. $q->execute();
  162. }
  163. elseif ($position < $newPosition)
  164. {
  165. $q = $object->getTable()->createQuery()
  166. ->update(get_class($object))
  167. ->set($this->_options['name'], $this->_options['name'] . ' - 1')
  168. ->where($this->_options['name'] . ' > ?', $position)
  169. ->andWhere($this->_options['name'] . ' <= ?', $newPosition)
  170. ->orderBy($this->_options['name'] . ' ASC');
  171. foreach($this->_options['uniqueBy'] as $field)
  172. {
  173. $q->addWhere($field . ' = ?', $object[$field]);
  174. }
  175. $q->execute();
  176. }
  177. $object->set($this->_options['name'], $newPosition);
  178. $object->save();
  179. // Commit Transaction
  180. $connection->commit();
  181. }
  182. /**
  183. * Send an array from the sortable_element tag (symfony+prototype)and it will
  184. * update the sort order to match
  185. *
  186. * @param string $order
  187. * @return void
  188. * @author Travis Black
  189. */
  190. public function sortTableProxy($order)
  191. {
  192. /*
  193. TODO
  194. - Add proper error messages.
  195. */
  196. $table = $this->getInvoker()->getTable();
  197. $class = get_class($this->getInvoker());
  198. $connection = $table->getConnection();
  199. $connection->beginTransaction();
  200. foreach ($order as $position => $id)
  201. {
  202. $newObject = Doctrine::getTable($class)->findOneById($id);
  203. if ($newObject->get($this->_options['name']) != $position + 1)
  204. {
  205. $newObject->moveToPosition($position + 1);
  206. }
  207. }
  208. // Commit Transaction
  209. $connection->commit();
  210. }
  211. /**
  212. * Finds all sortable objects and sorts them based on position attribute
  213. * Ascending or Descending based on parameter
  214. *
  215. * @param string $order
  216. * @return $query
  217. */
  218. public function findAllSortedTableProxy($order = 'ASC')
  219. {
  220. $order = $this->formatAndCheckOrder($order);
  221. $object = $this->getInvoker();
  222. $query = $object->getTable()->createQuery()
  223. ->orderBy($this->_options['name'] . ' ' . $order);
  224. return $query->execute();
  225. }
  226. /**
  227. * Finds and returns records sorted where the parent (fk) in a specified
  228. * one to many relationship has the value specified
  229. *
  230. * @param string $parentValue
  231. * @param string $parent_column_value
  232. * @param string $order
  233. * @return $query
  234. */
  235. public function findAllSortedWithParentTableProxy($parentValue, $parentColumnName = null, $order = 'ASC')
  236. {
  237. $order = $this->formatAndCheckOrder($order);
  238. $object = $this->getInvoker();
  239. $class = get_class($object);
  240. if (!$parentColumnName)
  241. {
  242. $parents = get_class($object->getParent());
  243. if (count($parents) > 1)
  244. {
  245. throw new Doctrine_Exception('No parent column name specified and object has mutliple parents');
  246. }
  247. elseif (count($parents) < 1)
  248. {
  249. throw new Doctrine_Exception('No parent column name specified and object has no parents');
  250. }
  251. else
  252. {
  253. $parentColumnName = $parents[0]->getType();
  254. exit((string) $parentColumnName);
  255. exit(print_r($parents[0]->toArray()));
  256. }
  257. }
  258. $query = $object->getTable()->createQuery()
  259. ->from($class . ' od')
  260. ->where('od.' . $parentColumnName . ' = ?', $parentValue)
  261. ->orderBy($this->_options['name'] . ' ' . $order);
  262. return $query->execute();
  263. }
  264. /**
  265. * Formats the ORDER for insertion in to query, else throws exception
  266. *
  267. * @param string $order
  268. * @return $order
  269. */
  270. public function formatAndCheckOrder($order)
  271. {
  272. $order = strtolower($order);
  273. if ('ascending' === $order || 'asc' === $order)
  274. {
  275. $order = 'ASC';
  276. }
  277. elseif ('descending' === $order || 'desc' === $order)
  278. {
  279. $order = 'DESC';
  280. }
  281. else
  282. {
  283. throw new Doctrine_Exception('Order parameter value must be "asc" or "desc"');
  284. }
  285. return $order;
  286. }
  287. /**
  288. * Get the final position of a model
  289. *
  290. * @return int $position
  291. */
  292. public function getFinalPosition()
  293. {
  294. $object = $this->getInvoker();
  295. $q = $object->getTable()->createQuery()
  296. ->select($this->_options['name'])
  297. ->orderBy($this->_options['name'] . ' desc');
  298. foreach($this->_options['uniqueBy'] as $field)
  299. {
  300. if(is_object($object[$field]))
  301. {
  302. $q->addWhere($field . ' = ?', $object[$field]['id']);
  303. }
  304. else
  305. {
  306. $q->addWhere($field . ' = ?', $object[$field]);
  307. }
  308. }
  309. $last = $q->limit(1)->fetchOne();
  310. $finalPosition = $last ? $last->get($this->_options['name']) : 0;
  311. return (int)$finalPosition;
  312. }
  313. }