Skip to content

Commit

Permalink
Merge pull request #24 from josegonzalez/patch-1
Browse files Browse the repository at this point in the history
Add ability to move up/down in list
  • Loading branch information
ADmad authored Aug 2, 2017
2 parents bb827fe + 6ccbe01 commit eb378c0
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 18 deletions.
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ matrix:
- php: 7.0
env: PHPCS=1 DEFAULT=0

- php: 7.0
env: PHPSTAN=1 DEFAULT=0

before_script:
- if [[ $TRAVIS_PHP_VERSION != 7.0 ]]; then phpenv config-rm xdebug.ini; fi

Expand All @@ -39,8 +42,11 @@ before_script:
script:
- if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION != 7.0 ]]; then vendor/bin/phpunit; fi
- if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.0 ]]; then vendor/bin/phpunit --coverage-clover=clover.xml; fi

- if [[ $PHPCS = 1 ]]; then vendor/bin/phpcs -n -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests; fi

- if [[ $PHPSTAN = 1 ]]; then composer require --dev phpstan/phpstan:^0.8 && vendor/bin/phpstan analyse -l 4 src; fi

after_success:
- if [[ $DEFAULT = 1 && $TRAVIS_PHP_VERSION = 7.0 ]]; then bash <(curl -s https://codecov.io/bash); fi

Expand Down
146 changes: 128 additions & 18 deletions src/Model/Behavior/SequenceBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use ArrayObject;
use Cake\Database\Expression\IdentifierExpression;
use Cake\Datasource\EntityInterface;
use Cake\Event\Event;
use Cake\ORM\Behavior;
use Cake\ORM\Entity;
Expand Down Expand Up @@ -127,23 +128,9 @@ public function beforeSave(Event $event, Entity $entity, ArrayObject $options)
$config = $this->config();

$newOrder = null;
$newScope = [];

// If scope are specified and data for all scope fields is not
// provided we cannot calculate new order
if ($config['scope']) {
$newScope = $entity->extract($config['scope']);
if (count($newScope) !== count($config['scope'])) {
return;
}

// Modify where clauses when NULL values are used
foreach ($newScope as $field => $value) {
if (is_null($value)) {
$newScope[$field . ' IS'] = $value;
unset($newScope[$field]);
}
}
$newScope = $this->_getScope($entity);
if ($newScope === false) {
return;
}

$orderField = $config['order'];
Expand Down Expand Up @@ -258,6 +245,97 @@ public function beforeDelete(Event $event, Entity $entity)
);
}

/**
* Decrease the position of the entity on the list
*
* If a "higher" entity exists, this will also swap positions with it
*
* @param \Cake\Datasource\EntityInterface $entity The entity that is going to be saved.
* @return bool
*/
public function moveUp(EntityInterface $entity)
{
return $this->_movePosition($entity, $direction = '-');
}

/**
* Increase the position of the entity on the list
*
* If a "lower" entity exists, this will also swap positions with it
*
* @param \Cake\Datasource\EntityInterface $entity The entity that is going to be saved.
* @return bool
*/
public function moveDown(EntityInterface $entity)
{
return $this->_movePosition($entity, $direction = '+');
}

/**
* Change the position of the entity on the list by a single position
*
* If an entity that conflicts with the new position already exists, this
* will also swap positions with it
*
* @param \Cake\Datasource\EntityInterface $entity The entity that is going to be saved.
* @param string $direction Whether to increment or decrement the field.
*
* @return bool
*/
protected function _movePosition(EntityInterface $entity, $direction = '+')
{
if ($entity->isNew()) {
return false;
}

$scope = $this->_getScope($entity);
if ($scope === false) {
return false;
}

$config = $this->config();
$table = $this->_table;

$table->removeBehavior('Sequence');

$return = $table->connection()->transactional(
function ($connection) use ($table, $entity, $config, $scope, $direction) {
$orderField = $config['order'];
// Nothing to do if trying to move up entity already at first position
if ($direction === '-' && $entity->get($orderField) === $config['start']) {
return true;
}

$oldOrder = $entity->get($orderField);
$newOrder = $entity->get($orderField) - 1;
if ($direction === '+') {
$newOrder = $entity->get($orderField) + 1;
}

$previousEntity = $table->find()
->where(array_merge($scope, [$orderField => $newOrder]))
->first();
if (!empty($previousEntity)) {
$previousEntity->set($orderField, $oldOrder);
if (!$table->save($previousEntity, ['atomic' => false, 'checkRules' => false])) {
return false;
}
// Nothing to do if trying to move down entity already at last position
} elseif ($direction === '+') {
return true;
}

$entity->set($orderField, $newOrder);

return $table->save($entity, ['atomic' => false, 'checkRules' => false]);
}
);

$table->addBehavior('ADmad/Sequence.Sequence', $config);

return (bool)$return;
}

/**
* Set order for list of records provided.
*
Expand All @@ -277,7 +355,7 @@ public function setOrder(array $records)
$table->removeBehavior('Sequence');

$return = $table->connection()->transactional(
function ($connection) use ($table, $records, $config) {
function ($connection) use ($table, $records) {
$order = $this->_config['start'];
$field = $this->_config['order'];

Expand Down Expand Up @@ -352,6 +430,38 @@ protected function _getOldValues(Entity $entity)
return [$order, $values];
}

/**
* Get scope values.
*
* @param \Cake\Datasource\EntityInterface $entity Entity.
*
* @return array|bool
*/
protected function _getScope(EntityInterface $entity)
{
$scope = [];
$config = $this->config();

// If scope are specified and data for all scope fields is not
// provided we cannot calculate new order
if ($config['scope']) {
$scope = $entity->extract($config['scope']);
if (count($scope) !== count($config['scope'])) {
return false;
}

// Modify where clauses when NULL values are used
foreach ($scope as $field => $value) {
if (is_null($value)) {
$scope[$field . ' IS'] = $value;
unset($scope[$field]);
}
}
}

return $scope;
}

/**
* Returns the current highest order of all records in the set. When a new
* record is added to the set, it is added at the current highest order, plus
Expand Down
88 changes: 88 additions & 0 deletions tests/TestCase/Model/Behavior/SequenceBehaviorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,94 @@ public function testSetOrder()
$this->assertOrder([6, 7, 8, 9, 10], $GroupedItems, ['group_field' => 2]);
}

/**
* testMoveUp
*
* @return void
*/
public function testMoveUp()
{
$Items = TableRegistry::get('Items', [
'className' => 'ADmad\Sequence\Test\TestCase\Model\Behavior\Items',
]);

$entity = $Items->get(4);
$result = $Items->moveUp($entity);

$this->assertTrue($result);
$this->assertOrder([1, 2, 4, 3, 5], $Items);

// Move up entity already at first position
$entity = $Items->get(1);
$result = $Items->moveUp($entity);

$this->assertTrue($result);
$this->assertOrder([1, 2, 4, 3, 5], $Items);

$GroupedItems = TableRegistry::get('GroupedItems', [
'table' => 'grouped_items',
'alias' => 'GroupedItems',
'className' => 'ADmad\Sequence\Test\TestCase\Model\Behavior\GroupedItems',
]);

$entity = $GroupedItems->get(4);
$result = $GroupedItems->moveUp($entity);

$this->assertTrue($result);
$this->assertOrder([1, 2, 4, 3, 5], $GroupedItems, ['group_field' => 1]);

// Move up entity already at first position
$entity = $GroupedItems->get(1);
$result = $GroupedItems->moveUp($entity);

$this->assertTrue($result);
$this->assertOrder([1, 2, 4, 3, 5], $GroupedItems, ['group_field' => 1]);
}

/**
* moveDown
*
* @return void
*/
public function testMoveDown()
{
$Items = TableRegistry::get('Items', [
'className' => 'ADmad\Sequence\Test\TestCase\Model\Behavior\Items',
]);

$entity = $Items->get(2);
$result = $Items->moveDown($entity);

$this->assertTrue($result);
$this->assertOrder([1, 3, 2, 4, 5], $Items);

// Move down entity already at last position
$entity = $Items->get(5);
$result = $Items->moveDown($entity);

$this->assertTrue($result);
$this->assertOrder([1, 3, 2, 4, 5], $Items);

$GroupedItems = TableRegistry::get('GroupedItems', [
'table' => 'grouped_items',
'alias' => 'GroupedItems',
'className' => 'ADmad\Sequence\Test\TestCase\Model\Behavior\GroupedItems',
]);

$entity = $GroupedItems->get(2);
$result = $GroupedItems->moveDown($entity);

$this->assertTrue($result);
$this->assertOrder([1, 3, 2, 4, 5], $GroupedItems, ['group_field' => 1]);

// Move down entity already at last position
$entity = $GroupedItems->get(5);
$result = $GroupedItems->moveDown($entity);

$this->assertTrue($result);
$this->assertOrder([1, 3, 2, 4, 5], $GroupedItems, ['group_field' => 1]);
}

/**
* [assertOrder description].
*
Expand Down

0 comments on commit eb378c0

Please sign in to comment.