chaw / branches / master / models / behaviors / list.php
history
<?php
/*
* List behavior for cakePHP
* comments, bug reports are welcome skie AT mail DOT ru
* @author Yevgeny Tomenko aka SkieDr
* @version 1.0.0.7
*
* modifed Dec 8, 2008 [gwoo]
*/
class ListBehavior extends ModelBehavior {
var $settings = array();
function setup(&$model, $config = array()) {
$this->listSetup($model, $config);
}
function listSetup(&$model, $config = array()) {
$settings = am(array(
'position_column' => 'position',
'scope' => '',
), $config);
$this->settings[$model->alias] = $settings;
}
/**
* Before save method. Called before all saves
*
* Overriden to transparently manage setting the item position to the end of the list
*
* @param AppModel $model
* @return boolean True to continue, false to abort the save
*/
function beforeSave(&$model) {
extract($this->settings[$model->alias]);
if (empty($model->data[$model->alias][$model->primaryKey])) {
$this->__addToListBottom($model);
}
return true;
}
/**
* Before delete method. Called before all deletes
*
* Will delete the current item from list and update position of all items after one
*
* @param AppModel $model
* @return boolean True to continue, false to abort the delete
*/
function beforeDelete(&$model) {
$dataStore = $model->data;
$model->recursive = 0;
$model->read(null,$model->id);
extract($this->settings[$model->alias]);
$result = $this->removeFromList($model);
$model->data = $dataStore;
return $result;
}
/**
* SetById method. Check is model innitialized.
*
* If $id is defined read record from model with this primary key value
*
* @param AppModel $model
* @param ID $id - value of model primary key to read
* @return boolean True if model initialized, false if no info in $model->data exists.
* @access private
*/
function __setById(&$model, $id=null, $checkId=true) {
if (!isset($id)) {
if ($checkId)
return isset($model->data[$model->alias]['id']);
else
return isset($model->data[$model->alias]);
} else {
return $model->read(null, $id);
}
}
/**
* Set new position of selected item for model
*
* @param AppModel $model
* @param int $position new position of item in list
* @param ID $id - value of model primary key to read
* @access public
*/
function insertAt(&$model,$position = 1, $id=null) {
if (!$this->__setById($model, $id, false)) {return false;}
return $this->__insertAtPosition($model,$position);
}
/**
* Swap positions with the next lower item, if one exists.
*
* @param AppModel $model
* @param ID $id - value of model primary key to read
* @access public
*/
function moveLower(&$model, $id=null) {
if (!$this->__setById($model, $id)) return false;
$lowerItem = $this->lowerItem($model);
if ($lowerItem==null) return;
# todo: add transaction
$currData=$model->data;
$model->set($lowerItem);
$this->_decrementPosition($model);
$model->set($currData);
return $this->_incrementPosition($model);
# todo: add transaction
}
/**
* Swap positions with the next higher item, if one exists.
*
* @param AppModel $model
* @param ID $id - value of model primary key to read
* @access public
*/
function moveHigher (&$model, $id=null) {
if (!$this->__setById($model, $id)) return false;
$higherItem = $this->higherItem($model);
if ($higherItem==null) return;
# todo: add transaction
$currData=$model->data;
$model->set($higherItem);
$this->_incrementPosition($model);
$model->set($currData);
return $this->_decrementPosition($model);
# todo: add transaction
}
/**
* Move to the bottom of the list. If the item is already in the list, the items below it have their
* position adjusted accordingly.
*
* @param AppModel $model
* @param ID $id - value of model primary key to read
* @access public
*/
function moveToBottom (&$model, $id=null) {
if (!$this->__setById($model, $id)) return false;
if (!$this->isInList($model)) return;
# todo: add transaction
$this->__decrementPositionsOnLowerItems($model);
return $this->__assumeBottomPosition($model);
# todo: add transaction
}
/**
* Move to the top of the list. If the item is already in the list, the items above it have their
* position adjusted accordingly.
*
* @param AppModel $model
* @param ID $id - value of model primary key to read
* @access public
*/
function moveToTop (&$model, $id=null) {
if (!$this->__setById($model, $id)) return false;
if (!$this->isInList($model)) return;
# todo: add transaction
$this->__incrementPositionsOnHigherItems($model);
return $this->__assumeTopPosition($model);
# todo: add transaction
}
function removeFromList(&$model, $id=null) {
if (!$this->__setById($model, $id)) return false;
if ($this->isInList($model)) return $this->__decrementPositionsOnLowerItems($model);
}
/**
* Increase the position of this item without adjusting the rest of the list.
*
* @param AppModel $model
* @access private
*/
function _incrementPosition(&$model) {
if (!$this->isInList($model)) return;
extract($this->settings[$model->alias]);
$model->data[$model->alias][$position_column]++;
return $model->save();
}
/**
* Decrease the position of this item without adjusting the rest of the list.
*
* @param AppModel $model
* @access private
*/
function _decrementPosition(&$model) {
if (!$this->isInList($model)) return;
extract($this->settings[$model->alias]);
$model->data[$model->alias][$position_column]--;
return $model->save();
}
/**
* Return true if this object is the first in the list.
*
* @param AppModel $model
* @access public
*/
function isFirst(&$model, $id=null) {
if (!$this->__setById($model, $id)) return false;
extract($this->settings[$model->alias]);
if (!$this->isInList($model)) return false;
return $model->data[$model->alias][$position_column]==1;
}
/**
* Return true if this object is the last in the list.
*
* @param AppModel $model
* @access public
*/
function isLast(&$model, $id=null) {
if (!$this->__setById($model, $id)) return false;
extract($this->settings[$model->alias]);
if (!$this->isInList($model)) return false;
return $model->data[$model->alias][$position_column]==$this->__bottomPositionInList($model);
}
/**
* Return the next higher item in the list.
*
* @param AppModel $model
* @access public
*/
function higherItem(&$model, $id=null) {
if (!$this->__setById($model, $id)) return false;
extract($this->settings[$model->alias]);
if (!$this->isInList($model)) return null;
$model->recursive = 0;
return $model->find(array($this->__scopeCondition($model), $position_column => $model->data[$model->alias][$position_column]-1));
}
/**
* Return the next lower item in the list.
*
* @param AppModel $model
* @access public
*/
function lowerItem(&$model, $id=null) {
if (!$this->__setById($model, $id)) return false;
extract($this->settings[$model->alias]);
if (!$this->isInList($model)) return null;
$model->recursive = 0;
return $model->find(array($this->__scopeCondition($model), $position_column => $model->data[$model->alias][$position_column]+1));
}
/**
* Return true if item in the list.
*
* @param AppModel $model
* @access public
*/
function isInList(&$model) {
extract($this->settings[$model->alias]);
if (empty($model->data[$model->alias][$position_column])) return false;
return !($model->data[$model->alias][$position_column] == null);
}
//private
function __scopeCondition(&$model) {
extract($this->settings[$model->alias]);
$scopes = array();
if (is_string($scope)) {
if ($scope=='') return $scopes;
if (substr($scope, -3) != '_id') {
$scope .= '_id';
}
$scopes[$model->alias . '.' . $scope] = $model->data[$model->alias][$scope]; //$model->alias.'.'.
} elseif (is_array($scope)) {
foreach ($scope as $scopeEl) {
if (substr($scopeEl, -3)=='_id') {
$scopeEl .= '_id';
}
$scopes[$model->alias . '.' . $scopeEl] = $model->data[$model->alias][$scopeEl]; //$model->alias.'.'.
}
}
//if (count($scopes)==0) $scopes[]="1=1";
return $scopes;
}
function __addToListTop(&$model) {
return $this->__incrementPositionsOnAllItems($model);
}
function __addToListBottom(&$model) {
extract($this->settings[$model->alias]);
$model->data[$model->alias][$position_column] = $this->__bottomPositionInList($model) + 1;
}
function __bottomPositionInList(&$model,$except = null) {
extract($this->settings[$model->alias]);
$item = $this->__bottomItem($model,$except);
if ($item) {
return $item[$model->alias][$position_column];
}
else {
return 0;
}
}
function __bottomItem(&$model,$except=null) {
extract($this->settings[$model->alias]);
$conditions = $this->__scopeCondition($model);
if (is_string($conditions)) $conditions=array($conditions);
if ($except!=null) $conditions = am ($conditions, array($model->alias.'.'.$model->primaryKey => "!= ".$except[$model->alias][$model->primaryKey]));
$model->recursive = 0;
return $model->find($conditions, null, array($model->alias .'.' . $position_column => 'DESC'));
}
function __assumeBottomPosition(&$model) {
extract($this->settings[$model->alias]);
$model->data[$model->alias][$position_column] = $this->__bottomPositionInList($model,$model->data)+1;
return $model->save();
}
function __assumeTopPosition(&$model) {
extract($this->settings[$model->alias]);
$model->data[$model->alias][$position_column] = 1;
return $model->save();
}
# This has the effect of moving all the higher items up one.
function __decrementPositionsOnHigherItems (&$model, $position) {
extract($this->settings[$model->alias]);
return $model->updateAll(array($model->alias .'.' . $position_column => $model->alias .'.' . $position_column .'-1'), array($this->__scopeCondition($model), $model->alias .'.' . $position_column => "<= $position"));
}
# This has the effect of moving all the lower items up one.
function __decrementPositionsOnLowerItems(&$model) {
if (!$this->isInList($model)) return;
extract($this->settings[$model->alias]);
return $model->updateAll(array($model->alias .'.' . $position_column => $model->alias .'.' . $position_column .'-1'), array($this->__scopeCondition($model), $model->alias .'.' . $position_column => "> ".$model->data[$model->alias][$position_column]));
}
# This has the effect of moving all the higher items down one.
function __incrementPositionsOnHigherItems (&$model) {
if (!$this->isInList($model)) return;
extract($this->settings[$model->alias]);
return $model->updateAll(array($model->alias .'.' . $position_column => $model->alias .'.' . $position_column . '+1'), array($this->__scopeCondition($model), $model->alias .'.' . $position_column => "< ".$model->data[$model->alias][$position_column]));
}
# This has the effect of moving all the lower items down one.
function __incrementPositionsOnLowerItems (&$model, $position) {
extract($this->settings[$model->alias]);
return $model->updateAll(array($model->alias .'.' . $position_column => $model->alias .'.' . $position_column .'+1'), array($this->__scopeCondition($model), $model->alias .'.' . $position_column => ">= $position"));
}
function __incrementPositionsOnAllItems (&$model) {
extract($this->settings[$model->alias]);
return $model->updateAll(array($model->alias .'.' . $position_column => $model->data[$model->alias][$position_column]+1), array($this->__scopeCondition($model)));
}
function __insertAtPosition(&$model, $position) {
extract($this->settings[$model->alias]);
$model->save();
$model->recursive = 0;
$model->findById($this->id);
$this->removeFromList($model);
$result=$this->__incrementPositionsOnLowerItems($model, $position);
if ($position<=$this->__bottomPositionInList($model)) {
$model->data[$model->alias][$position_column]=$position;
$result=$model->save();
}
return $result;
}
}
?>