3 * Copyright (c) 2008, Dardo Sordi
5 * Licensed under The MIT License
6 * Redistributions of files must retain the above copyright notice.
8 * @copyright Copyright (c) 2008, Dardo Sordi
9 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
13 class SortableBehavior
extends ModelBehavior
{
15 var $__config = array();
17 function setup(&$model, $config = array()) {
18 $default = array('field' => 'order', 'enabled' => true, 'cluster' => false);
19 $this->__config
[$model->name
] = array_merge($default, (array)$config);
22 function _config(&$model, $config = array()) {
23 if (is_array($config)) {
24 if (!empty($config)) {
25 $this->__config
[$model->name
] = array_merge($this->__config
[$model->name
], $config);
28 return $this->__config
[$model->name
][$config];
31 return $this->__config
[$model->name
];
34 function _fullField(&$model) {
35 return $model->name
. '.' . $this->_config($model, 'field');
38 function beforeFind(&$model, $conditions = array()) {
39 extract($this->_config($model));
43 if (is_string($conditions['fields']) && strpos($conditions['fields'], 'COUNT') === 0) {
46 $order = $conditions['order'];
47 if (is_array($conditions['order'])) {
48 $order = current($conditions['order']);
51 $conditions['order'] = array(array($this->_fullField($model) => 'ASC'));
53 if ($cluster !== false) {
54 $conditions['order'] = array_merge(array($cluster => 'ASC'), $conditions['order']);
60 function beforeSave(&$model) {
61 extract($this->_config($model));
64 $isInsert = !$model->id
;
65 if (isset($model->data
[$model->name
][$field]) && !empty($model->data
[$model->name
][$field])) {
66 $newPosition = $model->data
[$model->name
][$field];
70 $clusterId = $this->_clusterId($model);
71 $model->data
[$model->name
][$field] = $this->lastPosition($model, $clusterId) +
1;
72 $model->__fixPosition
= $newPosition;
77 function afterSave(&$model, $created) {
78 extract($this->_config($model));
82 $position = $model->data
[$model->name
][$field];
83 if ($model->__fixPosition
) {
84 $position = $model->__fixPosition
;
85 $model->__fixPosition
= null;
86 $this->setPosition($model, $model->id
, $position);
90 function beforeDelete(&$model) {
91 extract($this->_config($model));
93 $model->__fixPosition
= $this->position($model);
98 function afterDelete(&$model) {
99 extract($this->_config($model));
103 $fullField = $this->_fullField($model);
104 $position = $model->__fixPosition
;
105 $model->__fixPosition
= null;
106 $model->updateAll(array($fullField => "$fullField - 1"), array("$fullField >=" => $position));
109 function moveTop(&$model, $id = null) {
110 $this->disableSortable($model);
114 extract($this->_config($model));
115 $position = $this->position($model);
116 $clusterId = $this->_clusterId($model);
117 $fullField = $this->_fullField($model);
121 $conditions = $this->_conditions($model, $clusterId, array("$fullField <=" => $position, "$fullField >=" => $newPosition));
122 $model->updateAll(array($fullField => "$fullField + 1"), $conditions);
123 $model->saveField($field, $newPosition);
125 $this->enableSortable($model);
129 function moveUp(&$model, $id = null, $step = 1) {
130 $this->disableSortable($model);
134 extract($this->_config($model));
135 $position = $this->position($model);
136 $clusterId = $this->_clusterId($model);
137 $fullField = $this->_fullField($model);
140 $newPosition = $position - $step;
141 if ($newPosition < 1) {
144 $conditions = $this->_conditions($model, $clusterId, array("$fullField <=" => $position, "$fullField >=" => $newPosition));
145 $model->updateAll(array($fullField => "$fullField + 1"), $conditions);
146 $model->saveField($field, $newPosition);
148 $this->enableSortable($model);
152 function moveDown(&$model, $id = null, $step = 1) {
153 $this->disableSortable($model);
157 extract($this->_config($model));
158 $position = $this->position($model);
159 $clusterId = $this->_clusterId($model);
162 $last = $this->lastPosition($model, $clusterId);
163 $fullField = $this->_fullField($model);
165 if ($position < $last) {
166 $newPosition = $position +
$step;
167 if ($newPosition > $last) {
168 $newPosition = $last;
170 $conditions = $this->_conditions($model, $clusterId, array("$fullField >=" => $position, "$fullField <=" => $newPosition));
171 $model->updateAll(array($fullField => "$fullField - 1"), $conditions);
173 $model->saveField($field, $newPosition);
175 $this->enableSortable($model);
179 function moveBottom(&$model, $id = null) {
180 $this->disableSortable($model);
184 extract($this->_config($model));
186 $position = $this->position($model);
187 $clusterId = $this->_clusterId($model);
190 $last = $this->lastPosition($model, $clusterId);
191 $fullField = $this->_fullField($model);
193 if ($position < $last) {
194 $newPosition = $last;
195 $conditions = $this->_conditions($model, $clusterId, array("$fullField >=" => $position, "$fullField <=" => $newPosition));
196 $model->updateAll(array($fullField => "$fullField - 1"), $conditions);
198 $model->saveField($field, $newPosition);
200 $this->enableSortable($model);
204 function setPosition(&$model, $id = null, $destination) {
205 $this->disableSortable($model);
209 extract($this->_config($model));
210 $position = $this->position($model);
213 $delta = $position - $destination;
215 if ($position > $destination) {
216 $this->moveUp($model, $id, $delta);
217 } elseif ($position < $destination) {
218 $this->moveDown($model, $id, -$delta);
220 $this->enableSortable($model);
224 function disableSortable(&$model) {
225 $this->_config($model, array('enabled' => false));
228 function enableSortable(&$model) {
229 $this->_config($model, array('enabled' => true));
232 function position(&$model, $id = null) {
236 return $model->field($this->_config($model, 'field'));
239 function _clusterId(&$model, $id = null) {
240 $cluster = $this->_config($model, 'cluster');
242 if ($cluster === false) {
248 return $model->field($cluster);
251 function lastPosition(&$model, $clusterId = null) {
254 $field = $this->_config($model, 'field');
255 $fields = array($field);
256 $order = array($field => 'DESC');
257 $conditions = $this->_conditions($model, $clusterId);
258 $last = $model->find('first', compact('fields', 'order', 'conditions'));
262 return current(current($last));
268 function _conditions(&$model, $clusterId = null, $conditions = array()) {
269 $cluster = $this->_config($model, 'cluster');
271 if (($cluster !== false) && !is_null($clusterId)) {
272 $conditions = array_merge($conditions, array($cluster => $clusterId));
278 function findByPosition(&$model, $position, $clusterId = null) {
279 $field = $this->_fullField($model);
280 return $model->find($this->_conditions($model, $clusterId, array($field => $position)));