我在数据库表中使用了一个排序列。 每当我保存一个新的数据条目时,我想选择最大排序数,并用新条目保存这个新的排序号。
如何使用cakephp3进行此操作?
答案 0 :(得分:0)
我前一段时间发现了这段代码(遗憾的是,我不记得原作者的所有内容)并根据我的需要进行了编辑
将此添加到您要在
上使用排序的table
$this->addBehavior('Sortable', [
'field' => 'sort'
]);
并将其保存在/src/Model/Behaviour/SortableBehaviour.php
<?php
namespace App\Model\Behavior;
use Cake\Database\Expression\QueryExpression;
use Cake\Event\Event;
use Cake\ORM\Behavior;
use Cake\ORM\Entity;
use Cake\ORM\Exception\RecordNotFoundException;
use Cake\ORM\Table;
use Cake\ORM\Query;
class SortableBehavior extends Behavior {
protected $_table;
/**
* Default config
*
* These are merged with user-provided config when the behavior is used.
*
* field
* - the field in the DB-table that is doing the sorting
* scope
* - one or more foreign_keys as array
* - eg ['user_group_id'] in a table users would enabled sorting for each
* - user_group separately
* newEntries
* - either `last` or `first`
*
* @var array
*/
protected $_defaultConfig = [
'field' => 'sort',
'scope' => null,
'newEntries' => 'last'
];
/**
* Constructor
*
* @param \Cake\ORM\Table $table The table this behavior is attached to.
* @param array $config The config for this behavior.
*/
public function __construct(Table $table, array $config = []) {
parent::__construct($table, $config);
$this->_table = $table;
}
/**
* Before save listener
* handles initial sorting for added entities
* @param \Cake\Event\Event $event The beforeSave event that was fired
* @param \Cake\ORM\Entity $entity the entity that is going to be saved
* @return void
*/
public function beforeSave(Event $event, Entity $entity) {
$config = $this->config();
if (!$entity->isNew()) {
return;
}
if ($config['newEntries'] == 'last') {
$scopeData = $this->_getScopeData($entity);
$maxPosition = $this->_getMaxPosition($scopeData);
$entity->set($config['field'], $maxPosition + 1);
}
}
/**
* After delete listener
*
* makes sure the left over entities are sorted properly
*
* @param \Cake\Event\Event $event The afterDelete event that was fired
* @param \Cake\ORM\Entity $entity the entity that was going to be deleted
* @return void
*/
public function afterDelete(Event $event, Entity $entity) {
$config = $this->config();
$expression = new QueryExpression($config['field'] . ' = ' . $config['field'] . ' - 1');
$conditions = [
$config['field'].' >' => $entity->get($config['field'])
];
$conditions += $this->_getScopeData($entity);
$this->_table->updateAll($expression, $conditions);
}
/**
* main move method
*
*
*
* @param integer $id the id of the Entity that was moved
* @param array $newSortOrder all IDs of the current scope in the desired sort order (as provided by http://johnny.github.io/jquery-sortable/)
* @return boolean
*
*/
public function move($id, $newSortOrder) {
return $this->_table->connection()->transactional(function() use ($id, $newSortOrder) {
return $this->_move($id, $newSortOrder);
});
}
/**
* helper method that contains the actual move code
*
* @param integer $id the id of the Entity that was moved
* @param array $newSortOrder all IDs of the current scope in the desired sort order (as provided by http://johnny.github.io/jquery-sortable/)
* @return boolean
*
*/
protected function _move($id, $newSortOrder) {
$config = $this->config();
$node = $this->_getNode($id);
$current_position = $node[$config['field']];
// might be faster to do array_flip?!
$new_position = array_search($id, $newSortOrder) + 1;
// no position change -> return
if ($current_position == $new_position) {
return true;
}
// build sql for updating affected entries
if ($current_position < $new_position) {
$expression = new QueryExpression($config['field'] . ' = ' . $config['field'] . ' - 1');
$conditions = [
'sort >' => $current_position,
'AND' => [ $config['field'].' <=' => $new_position ],
];
} else {
$expression = new QueryExpression($config['field'] . ' = ' . $config['field'] . ' + 1');
$conditions = [
'sort <' => $current_position,
'AND' => [ $config['field'].' >=' => $new_position ],
];
}
$conditions += $this->_getScopeData($node);
$res = $this->_table->updateAll($expression, $conditions);
// finally set the inital field to the new val
$q = $this->_table->query();
return $res && $q->update()
->set([$config['field']=>$new_position])
->where(['id'=>$id])
->execute();
}
/**
* returns a node and the scope params if set up in config
* @param integer $id the node id
* @return Array|Object the result of query->first()
* @throws Cake\ORM\Exception\RecordNotFoundException if no node with that id found
*/
protected function _getNode($id) {
$config = $this->config();
// get current sort of `id`
// if a context is set -> get that also while we are aclling this
$query = $this->_table->find();
$query
->select(array_merge(['id', $config['field']], (array)$config['scope']))
->where(['id'=>$id])
->contain();
if (!$node = $query->first()) {
throw new RecordNotFoundException(sprintf('Kein Eintrag mit ID %s gefunden', $id));
}
return $node;
}
/**
* returns the highest position within that scope
*
* @param array $scopeData the scope data
* @return integer the currently highest positions in that scope
*/
protected function _getMaxPosition($scopeData=null) {
$config = $this->config();
$query = $this->_table->find();
$query
->select(array_merge([$config['field']], array_keys($scopeData)))
->where($scopeData)
->order([$config['field'] => 'DESC'])
->contain();
$node = $query->first();
return ($node[$config['field']] ?: 0);
}
protected function _getScopeData($node) {
$config = $this->config();
$conditions = [];
foreach ((array)$config['scope'] as $scope) {
$conditions[$scope] = $node[$scope];
}
return $conditions;
}
}
这会自动添加递增的排序值,也可以提供排序(您需要对控制器类执行操作)
希望有所帮助