├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── LICENSE ├── README.md ├── SortableBehavior.php ├── SortableTrait.php ├── composer.json ├── composer.lock ├── phpunit.xml.dist └── tests ├── .gitignore ├── ArrayDataSet.php ├── BaseTestCase.php ├── README.md ├── SortableBehaviorTestCase.php ├── bootstrap.php ├── data ├── config.php ├── data.php ├── empty.php ├── test-insert.php ├── test-move-after.php ├── test-move-before.php ├── test-move-first-insert-in-empty.php ├── test-move-first-insert-in-no-empty.php ├── test-move-first-update-other-scope.php ├── test-move-first-update-same-scope.php ├── test-move-last-insert-in-empty.php ├── test-move-last-insert-in-no-empty.php ├── test-move-last-update-other-scope.php ├── test-move-last-update-same-scope.php ├── test-move-to-insert-gap.php ├── test-move-to-insert-no-gap.php ├── test-move-to-update-other-scope.php ├── test-move-to-update-same-scope.php └── test-reorder.php ├── migrations └── TestMigration.php ├── models ├── Item.php └── ItemWindow.php ├── mysql └── SortableBehaviorTest.php ├── pgsql └── SortableBehaviorTest.php └── sqlite └── SortableBehaviorTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | # phpstorm project files 2 | .idea 3 | 4 | # windows thumbnail cache 5 | Thumbs.db 6 | 7 | # composer vendor dir 8 | /vendor 9 | 10 | # composer itself is not needed 11 | composer.phar 12 | 13 | # Mac DS_Store Files 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | tools: 2 | external_code_coverage: 3 | timeout: 2100 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - 7.0 8 | - hhvm 9 | 10 | sudo: false 11 | 12 | install: 13 | - composer self-update 14 | - composer global require fxp/composer-asset-plugin:~1.0 15 | - composer update --prefer-dist --no-interaction 16 | 17 | before_script: 18 | - mysql --version 19 | - psql --version 20 | - mysql -e 'create database test;' 21 | - psql -U postgres -c 'CREATE DATABASE test;'; 22 | - | 23 | if [ $TRAVIS_PHP_VERSION = '5.6' ]; then 24 | PHPUNIT_FLAGS="--coverage-clover=coverage.clover" 25 | fi 26 | 27 | script: 28 | - vendor/bin/phpunit $PHPUNIT_FLAGS 29 | 30 | after_script: 31 | - | 32 | if [ $TRAVIS_PHP_VERSION = '5.6' ]; then 33 | wget https://scrutinizer-ci.com/ocular.phar 34 | php ocular.phar code-coverage:upload --format=php-clover coverage.clover 35 | fi 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 PaulZi (pavel.zimakoff@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yii2 Sortable Behavior 2 | 3 | It implements the ability to control the order of the ActiveRecord. 4 | 5 | [![Packagist Version](https://img.shields.io/packagist/v/paulzi/yii2-sortable.svg)](https://packagist.org/packages/paulzi/yii2-sortable) 6 | [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/paulzi/yii2-sortable/master.svg)](https://scrutinizer-ci.com/g/paulzi/yii2-sortable/?branch=master) 7 | [![Build Status](https://img.shields.io/travis/paulzi/yii2-sortable/master.svg)](https://travis-ci.org/paulzi/yii2-sortable) 8 | [![Total Downloads](https://img.shields.io/packagist/dt/paulzi/yii2-sortable.svg)](https://packagist.org/packages/paulzi/yii2-sortable) 9 | 10 | ## Install 11 | 12 | Install via Composer: 13 | 14 | ```bash 15 | composer require paulzi/yii2-sortable 16 | ``` 17 | 18 | or add 19 | 20 | ```bash 21 | "paulzi/yii2-sortable" : "^1.0" 22 | ``` 23 | 24 | to the `require` section of your `composer.json` file. 25 | 26 | ## Migrations 27 | 28 | Add signed integer column to your model. 29 | For quick operation behavior, add index for scopes fields and sort attribute, example: 30 | ```php 31 | $this->createIndex('parent_sort', '{{%item}}', ['parent_id', 'sort']); 32 | ``` 33 | 34 | ## Configuring 35 | 36 | ```php 37 | use paulzi\sortable\SortableBehavior; 38 | 39 | class Sample extends \yii\db\ActiveRecord 40 | { 41 | public function behaviors() { 42 | return [ 43 | [ 44 | 'class' => SortableBehavior::className(), 45 | ], 46 | ]; 47 | } 48 | } 49 | ``` 50 | 51 | ## Options 52 | 53 | - `$query = null` - list of attributes, callback or ActiveQuery, to find scope element. See below. 54 | - `$sortAttribute = 'sort'` - sort attribute in table schema. 55 | - `$step = 100` - gap size between elements. 56 | - `$joinMode = true` - search method of unallocated value. When joinMode is true, using join table with self. Otherwise, use the search in the window. Window size defined by $windowSize property. 57 | - `$windowSize = 1000` - defines the size of the search window, when joinMode is false. 58 | 59 | **Details of `$query` option:** 60 | The list of attributes - a simple way to scope elements with the same content fields, the aliases do not need. 61 | You MUST use tableName() alias in ActiveQuery, when you are using joinMode. 62 | 63 | For example, 64 | ```php 65 | public function behaviors() 66 | { 67 | return [ 68 | [ 69 | 'class' => SortableBehavior::className(), 70 | 'query' => ['parent_id'], 71 | ] 72 | ]; 73 | } 74 | ``` 75 | 76 | This is equivalent to: 77 | ```php 78 | public function behaviors() 79 | { 80 | return [ 81 | [ 82 | 'class' => SortableBehavior::className(), 83 | 'query' => function ($model) { 84 | $tableName = $model->tableName(); 85 | return $model->find()->andWhere(["{$tableName}.[[parent_id]]" => $model->parent_id]); 86 | }, 87 | ] 88 | ]; 89 | } 90 | ``` 91 | 92 | ## Usage 93 | 94 | Getting sort attribute value: 95 | 96 | ```php 97 | $model = Sample::findOne(1); 98 | $position = $model->getSortablePosition(); 99 | ``` 100 | 101 | To move as the first item: 102 | 103 | ```php 104 | $model = new Sample(['parent_id' => 1]); 105 | $model->moveFirst()->save(); // inserting new node 106 | ``` 107 | 108 | To move as the last item: 109 | 110 | ```php 111 | $model = Sample::findOne(1); 112 | $model->moveLast()->save(); // move existing node 113 | ``` 114 | 115 | To move an item to a specific position: 116 | 117 | ```php 118 | $model = Sample::findOne(1); 119 | $model->moveTo(-33, true)->save(); // move to position -33, and move existing items forward 120 | $model = Sample::findOne(2); 121 | $model->moveTo(4, false)->save(); // move to position 4, and move existing items backward 122 | ``` 123 | 124 | To move an item before another: 125 | *Note: If you need to change scope, do it manually* 126 | 127 | ```php 128 | $model1 = new Sample(['parent_id' => 1]); 129 | $model2 = Sample::findOne(2); 130 | $model1->moveBefore($model2)->save(); // move $model1 before $model2 131 | ``` 132 | 133 | To move an item after another: 134 | *Note: If you need to change scope, do it manually* 135 | 136 | ```php 137 | $model1 = Sample::findOne(1); 138 | $model2 = Sample::findOne(2); 139 | $model1->parent_id = $model2->parent_id; 140 | $model1->moveAfter($model2)->save(); // move $model1 after $model2 with change scope 141 | ``` 142 | 143 | Reorder item with the neighboring elements: 144 | 145 | ```php 146 | $model = Sample::findOne(1); 147 | $model->reorder(true); // reorder with center zero 148 | $model = Sample::findOne(2); 149 | $model->reorder(false); // reorder from zero 150 | ``` 151 | 152 | ## SortableTrait 153 | 154 | You can use optional `SortableTrait`, if you need it (for example, you are using something like `ISortable` interface). -------------------------------------------------------------------------------- /SortableBehavior.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable; 9 | 10 | use yii\base\Behavior; 11 | use yii\db\ActiveQuery; 12 | use yii\db\ActiveRecord; 13 | use yii\db\Expression; 14 | 15 | 16 | /** 17 | * Sortable Behavior for Yii2 18 | * @author PaulZi 19 | * 20 | * @property ActiveRecord $owner 21 | */ 22 | class SortableBehavior extends Behavior 23 | { 24 | const OPERATION_FIRST = 1; 25 | const OPERATION_LAST = 2; 26 | const OPERATION_POSITION_BACKWARD = 3; 27 | const OPERATION_POSITION_FORWARD = 4; 28 | 29 | /** 30 | * List of attributes, callable or ActiveQuery 31 | * The list of attributes - a simple way to scope elements with the same content fields, the aliases do not need. 32 | * Warning! You MUST use tableName() alias in ActiveQuery, when you are using joinMode: 33 | * For example, 34 | * 35 | * ~~~ 36 | * public function behaviors() 37 | * { 38 | * return [ 39 | * [ 40 | * 'class' => SortableBehavior::className(), 41 | * 'query' => ['parent_id'], 42 | * ] 43 | * ]; 44 | * } 45 | * ~~~ 46 | * 47 | * This is equivalent to: 48 | * 49 | * ~~~ 50 | * public function behaviors() 51 | * { 52 | * return [ 53 | * [ 54 | * 'class' => SortableBehavior::className(), 55 | * 'query' => function ($model) { 56 | * $tableName = $model->tableName(); 57 | * return $model->find()->andWhere(["{$tableName}.[[parent_id]]" => $model->parent_id]); 58 | * }, 59 | * ] 60 | * ]; 61 | * } 62 | * ~~~ 63 | * 64 | * @var array|callable|ActiveQuery 65 | */ 66 | public $query; 67 | 68 | /** 69 | * @var string 70 | */ 71 | public $sortAttribute = 'sort'; 72 | 73 | /** 74 | * @var int 75 | */ 76 | public $step = 100; 77 | 78 | /** 79 | * Search method of unallocated value. 80 | * When joinMode is true, using join table with self. Otherwise, use the search in the window. Window size defined by $windowSize property. 81 | * @var bool 82 | */ 83 | public $joinMode = true; 84 | 85 | /** 86 | * Defines the size of the search window, when joinMode is false. 87 | * @var int 88 | */ 89 | public $windowSize = 1000; 90 | 91 | /** 92 | * @var integer|null 93 | */ 94 | protected $operation; 95 | 96 | /** 97 | * @var integer 98 | */ 99 | protected $position; 100 | 101 | 102 | /** 103 | * @inheritdoc 104 | */ 105 | public function events() 106 | { 107 | return [ 108 | ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave', 109 | ActiveRecord::EVENT_AFTER_INSERT => 'afterSave', 110 | ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeSave', 111 | ActiveRecord::EVENT_AFTER_UPDATE => 'afterSave', 112 | ]; 113 | } 114 | 115 | /** 116 | * @return integer 117 | */ 118 | public function getSortablePosition() 119 | { 120 | return $this->owner->getAttribute($this->sortAttribute); 121 | } 122 | 123 | /** 124 | * @return ActiveRecord 125 | */ 126 | public function moveFirst() 127 | { 128 | $this->operation = self::OPERATION_FIRST; 129 | return $this->owner; 130 | } 131 | 132 | /** 133 | * @return ActiveRecord 134 | */ 135 | public function moveLast() 136 | { 137 | $this->operation = self::OPERATION_LAST; 138 | return $this->owner; 139 | } 140 | 141 | /** 142 | * @param integer $position 143 | * @param bool $forward Move existing items to forward or backward 144 | * @return ActiveRecord 145 | */ 146 | public function moveTo($position, $forward = true) 147 | { 148 | $this->operation = $forward ? self::OPERATION_POSITION_FORWARD : self::OPERATION_POSITION_BACKWARD; 149 | $this->position = (int)$position; 150 | return $this->owner; 151 | } 152 | 153 | /** 154 | * @param ActiveRecord $model 155 | * @return ActiveRecord 156 | */ 157 | public function moveBefore($model) 158 | { 159 | return $this->moveTo($model->getAttribute($this->sortAttribute) - 1, false); 160 | } 161 | 162 | /** 163 | * @param ActiveRecord $model 164 | * @return ActiveRecord 165 | */ 166 | public function moveAfter($model) 167 | { 168 | return $this->moveTo($model->getAttribute($this->sortAttribute) + 1, true); 169 | } 170 | 171 | /** 172 | * Reorders items with values of sortAttribute begin from zero. 173 | * @param bool $middle 174 | * @return integer 175 | * @throws \Exception 176 | */ 177 | public function reorder($middle = true) 178 | { 179 | $result = 0; 180 | \Yii::$app->getDb()->transaction(function () use (&$result, $middle) { 181 | $list = $this->getQueryInternal() 182 | ->select($this->owner->primaryKey()) 183 | ->orderBy([$this->sortAttribute => SORT_ASC]) 184 | ->asArray() 185 | ->all(); 186 | $from = $middle ? count($list) >> 1 : 0; 187 | foreach ($list as $i => $item) { 188 | $result += $this->owner->updateAll([$this->sortAttribute => ($i - $from) * $this->step], $item); 189 | } 190 | }); 191 | 192 | return $result; 193 | } 194 | 195 | /** 196 | * 197 | */ 198 | public function beforeSave() 199 | { 200 | if ($this->owner->getIsNewRecord() && $this->operation === null) { 201 | $this->operation = self::OPERATION_LAST; 202 | } 203 | 204 | switch ($this->operation) { 205 | case self::OPERATION_FIRST: 206 | case self::OPERATION_LAST: 207 | $query = $this->getQueryInternal(); 208 | $query->orderBy(null); 209 | $position = $this->operation === self::OPERATION_LAST ? $query->max("[[$this->sortAttribute]]") : $query->min("[[$this->sortAttribute]]"); 210 | 211 | $isSelf = false; 212 | if ($position !== null && !$this->owner->getIsNewRecord() && (int)$position === $this->owner->getAttribute($this->sortAttribute)) { 213 | if ($this->query instanceof ActiveQuery || is_callable($this->query)) { 214 | $isSelf = $this->getQueryInternal() 215 | ->andWhere([$this->sortAttribute => $position]) 216 | ->andWhere($this->selfCondition()) 217 | ->exists(); 218 | 219 | } else { 220 | $isSelf = count($this->owner->getDirtyAttributes($this->query)) === 0; 221 | } 222 | } 223 | 224 | if ($position === null) { 225 | $this->owner->setAttribute($this->sortAttribute, 0); 226 | } elseif (!$isSelf) { 227 | if ($this->operation === self::OPERATION_LAST) { 228 | $this->owner->setAttribute($this->sortAttribute, $position + $this->step); 229 | } else { 230 | $this->owner->setAttribute($this->sortAttribute, $position - $this->step); 231 | } 232 | } 233 | break; 234 | 235 | case self::OPERATION_POSITION_BACKWARD: 236 | case self::OPERATION_POSITION_FORWARD: 237 | $this->moveToInternal($this->position, $this->operation === self::OPERATION_POSITION_FORWARD); 238 | break; 239 | } 240 | } 241 | 242 | /** 243 | * 244 | */ 245 | public function afterSave() 246 | { 247 | $this->operation = null; 248 | } 249 | 250 | /** 251 | * @return ActiveQuery 252 | */ 253 | protected function getQueryInternal() 254 | { 255 | if ($this->query instanceof ActiveQuery) { 256 | $query = clone $this->query; 257 | return $query; 258 | } elseif (is_callable($this->query)) { 259 | return call_user_func($this->query, $this->owner); 260 | } else { 261 | $tableName = $this->owner->tableName(); 262 | $attributes = $this->owner->getAttributes($this->query); 263 | $attributes = array_combine( 264 | array_map(function ($value) use ($tableName) { return "{$tableName}.[[{$value}]]"; }, array_keys($attributes)), 265 | array_values($attributes) 266 | ); 267 | return $this->owner->find()->andWhere($attributes); 268 | } 269 | } 270 | 271 | /** 272 | * @param string $tableName 273 | * @param string $string 274 | * @return string 275 | */ 276 | protected static function getJoinConditionReplace($tableName, $string) 277 | { 278 | return str_replace($tableName . '.', 'n.', $string); 279 | } 280 | 281 | /** 282 | * @param string $tableName 283 | * @param array|string $condition 284 | * @return array|string 285 | */ 286 | protected static function getJoinCondition($tableName, $condition) 287 | { 288 | if (is_string($condition)) { 289 | return static::getJoinConditionReplace($tableName, $condition); 290 | } elseif (is_array($condition)) { 291 | $joinCondition = []; 292 | array_walk($condition, function ($value, $key) use ($tableName, &$joinCondition) { 293 | $joinCondition[static::getJoinConditionReplace($tableName, $key)] = static::getJoinCondition($tableName, $value); 294 | }); 295 | return $joinCondition; 296 | } elseif ($condition instanceof Expression) { 297 | $condition->expression = static::getJoinConditionReplace($tableName, $condition->expression); 298 | } 299 | return $condition; 300 | } 301 | 302 | /** 303 | * @return array 304 | */ 305 | protected function selfCondition() 306 | { 307 | $tableName = $this->owner->tableName(); 308 | $result = []; 309 | foreach ($this->owner->getPrimaryKey(true) as $field => $value) { 310 | $result["{$tableName}.[[{$field}]]"] = $value; 311 | } 312 | return $result; 313 | } 314 | 315 | /** 316 | * @param integer $from 317 | * @param integer|null $to 318 | * @param bool $forward 319 | */ 320 | protected function shift($from, $to, $forward) 321 | { 322 | $query = $this->getQueryInternal(); 323 | if ($to === null) { 324 | $condition = [$forward ? '>=' : '<=', $this->sortAttribute, $from]; 325 | } else { 326 | $condition = ['between', $this->sortAttribute, $forward ? $from : $to, $forward ? $to : $from]; 327 | } 328 | $this->owner->updateAll( 329 | [$this->sortAttribute => new Expression("[[{$this->sortAttribute}]] " . ($forward ? '+ 1' : '- 1'))], 330 | [ 331 | 'and', 332 | $query->where, 333 | $condition, 334 | ] 335 | ); 336 | } 337 | 338 | /** 339 | * @param integer $position 340 | * @param bool $forward 341 | */ 342 | protected function moveToInternal($position, $forward) 343 | { 344 | if ($this->joinMode) { 345 | $this->moveToInternalJoinMode($position, $forward); 346 | } else { 347 | $this->moveToInternalWindowMode($position, $forward); 348 | } 349 | } 350 | 351 | /** 352 | * @param integer $position 353 | * @param bool $forward 354 | */ 355 | protected function moveToInternalJoinMode($position, $forward) 356 | { 357 | $this->owner->setAttribute($this->sortAttribute, $position); 358 | 359 | $tableName = $this->owner->tableName(); 360 | $query = $this->getQueryInternal(); 361 | $joinCondition = [ 362 | 'and', 363 | static::getJoinCondition($tableName, $query->where), 364 | ["n.[[{$this->sortAttribute}]]" => new Expression("{$tableName}.[[{$this->sortAttribute}]] " . ($forward ? '+ 1' : '- 1'))], 365 | ]; 366 | if (!$this->owner->getIsNewRecord()) { 367 | $joinCondition[] = ['not', static::getJoinCondition($tableName, $this->selfCondition())]; 368 | } 369 | 370 | $exists = $query 371 | ->andWhere(["{$tableName}.[[{$this->sortAttribute}]]" => $position]) 372 | ->andWhere(['not', $this->selfCondition()]) 373 | ->exists(); 374 | if ($exists) { 375 | $unallocated = $this->getQueryInternal() 376 | ->select("{$tableName}.[[{$this->sortAttribute}]]") 377 | ->leftJoin("{$tableName} n", $joinCondition) 378 | ->andWhere([ 379 | 'and', 380 | [$forward ? '>=' : '<=', "{$tableName}.[[{$this->sortAttribute}]]", $position - ($forward ? 1 : -1)], 381 | ["n.[[{$this->sortAttribute}]]" => null], 382 | ]) 383 | ->orderBy(["{$tableName}.[[{$this->sortAttribute}]]" => $forward ? SORT_ASC : SORT_DESC]) 384 | ->limit(1) 385 | ->scalar(); 386 | $this->shift($position, $unallocated, $forward); 387 | } 388 | } 389 | 390 | /** 391 | * @param integer $position 392 | * @param bool $forward 393 | */ 394 | protected function moveToInternalWindowMode($position, $forward) 395 | { 396 | $this->owner->setAttribute($this->sortAttribute, $position); 397 | 398 | $tableName = $this->owner->tableName(); 399 | $query = $this->getQueryInternal(); 400 | if (!$this->owner->getIsNewRecord()) { 401 | $query->andWhere(['not', $this->selfCondition()]); 402 | } 403 | 404 | $list = $query 405 | ->select("{$tableName}.[[{$this->sortAttribute}]]") 406 | ->andWhere([$forward ? '>=' : '<=', "{$tableName}.[[{$this->sortAttribute}]]", $position]) 407 | ->orderBy(["{$tableName}.[[{$this->sortAttribute}]]" => $forward ? SORT_ASC : SORT_DESC]) 408 | ->limit($this->windowSize) 409 | ->column(); 410 | $unallocated = null; 411 | $prev = $position - ($forward ? 1 : -1); 412 | foreach ($list as $item) { 413 | if (abs($item - $prev) > 1) { 414 | $unallocated = $prev; 415 | break; 416 | } 417 | $prev = $item; 418 | } 419 | 420 | $this->shift($position, $unallocated, $forward); 421 | } 422 | } 423 | -------------------------------------------------------------------------------- /SortableTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable; 9 | 10 | use yii\base\InvalidConfigException; 11 | use yii\db\ActiveRecord; 12 | 13 | /** 14 | * Sortable Trait 15 | * @author PaulZi 16 | */ 17 | trait SortableTrait 18 | { 19 | /** 20 | * @var SortableBehavior 21 | */ 22 | private $_sortableBehavior; 23 | 24 | 25 | /** 26 | * @throws InvalidConfigException 27 | */ 28 | private function getSortableBehavior() 29 | { 30 | if ($this->_sortableBehavior === null) { 31 | /** @var \yii\base\Component|self $this */ 32 | foreach ($this->getBehaviors() as $behavior) { 33 | if ($behavior instanceof SortableBehavior) { 34 | $this->_sortableBehavior = $behavior; 35 | } 36 | } 37 | if ($this->_sortableBehavior === null) { 38 | throw new InvalidConfigException('SortableBehavior is not attached to model'); 39 | } 40 | } 41 | return $this->_sortableBehavior; 42 | } 43 | 44 | /** 45 | * @return integer 46 | */ 47 | public function getSortablePosition() 48 | { 49 | return $this->getSortableBehavior()->getSortablePosition(); 50 | } 51 | 52 | /** 53 | * @return $this 54 | */ 55 | public function moveFirst() 56 | { 57 | return $this->getSortableBehavior()->moveFirst(); 58 | } 59 | 60 | /** 61 | * @return $this 62 | */ 63 | public function moveLast() 64 | { 65 | return $this->getSortableBehavior()->moveLast(); 66 | } 67 | 68 | /** 69 | * @param integer $position 70 | * @param bool $forward Move existing items to forward or backward 71 | * @return $this 72 | */ 73 | public function moveTo($position, $forward = true) 74 | { 75 | return $this->getSortableBehavior()->moveTo($position, $forward); 76 | } 77 | 78 | /** 79 | * @param ActiveRecord $model 80 | * @return $this 81 | */ 82 | public function moveBefore($model) 83 | { 84 | return $this->getSortableBehavior()->moveBefore($model); 85 | } 86 | 87 | /** 88 | * @param ActiveRecord $model 89 | * @return $this 90 | */ 91 | public function moveAfter($model) 92 | { 93 | return $this->getSortableBehavior()->moveAfter($model); 94 | } 95 | 96 | /** 97 | * Reorders items with values of sortAttribute begin from zero. 98 | * @param bool $middle 99 | * @return integer 100 | * @throws \Exception 101 | */ 102 | public function reorder($middle = true) 103 | { 104 | return $this->getSortableBehavior()->reorder($middle); 105 | } 106 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paulzi/yii2-sortable", 3 | "description": "Sortable Behavior for Yii2", 4 | "keywords": ["yii2", "sortable"], 5 | "type": "yii2-extension", 6 | "license": "MIT", 7 | "support": { 8 | "issues": "https://github.com/paulzi/yii2-sortable/issues?state=open", 9 | "source": "https://github.com/paulzi/yii2-sortable" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "PaulZi", 14 | "email": "pavel.zimakoff@gmail.com" 15 | } 16 | ], 17 | "minimum-stability": "stable", 18 | "require": { 19 | "php": ">=5.4.0", 20 | "yiisoft/yii2": "~2.0.0" 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "~4.0", 24 | "phpunit/dbunit": "~1.0" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "paulzi\\sortable\\": "" 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "8730eca8ceff78ed1327996ec260b428", 8 | "content-hash": "5ec5565227dd7beb3bcb4d8e3e930652", 9 | "packages": [ 10 | { 11 | "name": "bower-asset/jquery", 12 | "version": "2.2.4", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/jquery/jquery-dist.git", 16 | "reference": "c0185ab7c75aab88762c5aae780b9d83b80eda72" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/c0185ab7c75aab88762c5aae780b9d83b80eda72", 21 | "reference": "c0185ab7c75aab88762c5aae780b9d83b80eda72", 22 | "shasum": "" 23 | }, 24 | "type": "bower-asset-library", 25 | "extra": { 26 | "bower-asset-main": "dist/jquery.js", 27 | "bower-asset-ignore": [ 28 | "package.json" 29 | ] 30 | }, 31 | "license": [ 32 | "MIT" 33 | ], 34 | "keywords": [ 35 | "browser", 36 | "javascript", 37 | "jquery", 38 | "library" 39 | ] 40 | }, 41 | { 42 | "name": "bower-asset/jquery.inputmask", 43 | "version": "3.2.7", 44 | "source": { 45 | "type": "git", 46 | "url": "https://github.com/RobinHerbots/jquery.inputmask.git", 47 | "reference": "5a72c563b502b8e05958a524cdfffafe9987be38" 48 | }, 49 | "dist": { 50 | "type": "zip", 51 | "url": "https://api.github.com/repos/RobinHerbots/jquery.inputmask/zipball/5a72c563b502b8e05958a524cdfffafe9987be38", 52 | "reference": "5a72c563b502b8e05958a524cdfffafe9987be38", 53 | "shasum": "" 54 | }, 55 | "require": { 56 | "bower-asset/jquery": ">=1.7" 57 | }, 58 | "type": "bower-asset-library", 59 | "extra": { 60 | "bower-asset-main": [ 61 | "./dist/inputmask/inputmask.js" 62 | ], 63 | "bower-asset-ignore": [ 64 | "**/*", 65 | "!dist/*", 66 | "!dist/inputmask/*", 67 | "!dist/min/*", 68 | "!dist/min/inputmask/*", 69 | "!extra/bindings/*", 70 | "!extra/dependencyLibs/*", 71 | "!extra/phone-codes/*" 72 | ] 73 | }, 74 | "license": [ 75 | "http://opensource.org/licenses/mit-license.php" 76 | ], 77 | "description": "jquery.inputmask is a jquery plugin which create an input mask.", 78 | "keywords": [ 79 | "form", 80 | "input", 81 | "inputmask", 82 | "jquery", 83 | "mask", 84 | "plugins" 85 | ] 86 | }, 87 | { 88 | "name": "bower-asset/punycode", 89 | "version": "v1.3.2", 90 | "source": { 91 | "type": "git", 92 | "url": "https://github.com/bestiejs/punycode.js.git", 93 | "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3" 94 | }, 95 | "dist": { 96 | "type": "zip", 97 | "url": "https://api.github.com/repos/bestiejs/punycode.js/zipball/38c8d3131a82567bfef18da09f7f4db68c84f8a3", 98 | "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3", 99 | "shasum": "" 100 | }, 101 | "type": "bower-asset-library", 102 | "extra": { 103 | "bower-asset-main": "punycode.js", 104 | "bower-asset-ignore": [ 105 | "coverage", 106 | "tests", 107 | ".*", 108 | "component.json", 109 | "Gruntfile.js", 110 | "node_modules", 111 | "package.json" 112 | ] 113 | } 114 | }, 115 | { 116 | "name": "bower-asset/yii2-pjax", 117 | "version": "v2.0.6", 118 | "source": { 119 | "type": "git", 120 | "url": "https://github.com/yiisoft/jquery-pjax.git", 121 | "reference": "60728da6ade5879e807a49ce59ef9a72039b8978" 122 | }, 123 | "dist": { 124 | "type": "zip", 125 | "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/60728da6ade5879e807a49ce59ef9a72039b8978", 126 | "reference": "60728da6ade5879e807a49ce59ef9a72039b8978", 127 | "shasum": "" 128 | }, 129 | "require": { 130 | "bower-asset/jquery": ">=1.8" 131 | }, 132 | "type": "bower-asset-library", 133 | "extra": { 134 | "bower-asset-main": "./jquery.pjax.js", 135 | "bower-asset-ignore": [ 136 | ".travis.yml", 137 | "Gemfile", 138 | "Gemfile.lock", 139 | "CONTRIBUTING.md", 140 | "vendor/", 141 | "script/", 142 | "test/" 143 | ] 144 | }, 145 | "license": [ 146 | "MIT" 147 | ] 148 | }, 149 | { 150 | "name": "cebe/markdown", 151 | "version": "1.1.0", 152 | "source": { 153 | "type": "git", 154 | "url": "https://github.com/cebe/markdown.git", 155 | "reference": "54a2c49de31cc44e864ebf0500a35ef21d0010b2" 156 | }, 157 | "dist": { 158 | "type": "zip", 159 | "url": "https://api.github.com/repos/cebe/markdown/zipball/54a2c49de31cc44e864ebf0500a35ef21d0010b2", 160 | "reference": "54a2c49de31cc44e864ebf0500a35ef21d0010b2", 161 | "shasum": "" 162 | }, 163 | "require": { 164 | "lib-pcre": "*", 165 | "php": ">=5.4.0" 166 | }, 167 | "require-dev": { 168 | "cebe/indent": "*", 169 | "facebook/xhprof": "*@dev", 170 | "phpunit/phpunit": "4.1.*" 171 | }, 172 | "bin": [ 173 | "bin/markdown" 174 | ], 175 | "type": "library", 176 | "extra": { 177 | "branch-alias": { 178 | "dev-master": "1.1.x-dev" 179 | } 180 | }, 181 | "autoload": { 182 | "psr-4": { 183 | "cebe\\markdown\\": "" 184 | } 185 | }, 186 | "notification-url": "https://packagist.org/downloads/", 187 | "license": [ 188 | "MIT" 189 | ], 190 | "authors": [ 191 | { 192 | "name": "Carsten Brandt", 193 | "email": "mail@cebe.cc", 194 | "homepage": "http://cebe.cc/", 195 | "role": "Creator" 196 | } 197 | ], 198 | "description": "A super fast, highly extensible markdown parser for PHP", 199 | "homepage": "https://github.com/cebe/markdown#readme", 200 | "keywords": [ 201 | "extensible", 202 | "fast", 203 | "gfm", 204 | "markdown", 205 | "markdown-extra" 206 | ], 207 | "time": "2015-03-06 05:28:07" 208 | }, 209 | { 210 | "name": "ezyang/htmlpurifier", 211 | "version": "v4.8.0", 212 | "source": { 213 | "type": "git", 214 | "url": "https://github.com/ezyang/htmlpurifier.git", 215 | "reference": "d0c392f77d2f2a3dcf7fcb79e2a1e2b8804e75b2" 216 | }, 217 | "dist": { 218 | "type": "zip", 219 | "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/d0c392f77d2f2a3dcf7fcb79e2a1e2b8804e75b2", 220 | "reference": "d0c392f77d2f2a3dcf7fcb79e2a1e2b8804e75b2", 221 | "shasum": "" 222 | }, 223 | "require": { 224 | "php": ">=5.2" 225 | }, 226 | "type": "library", 227 | "autoload": { 228 | "psr-0": { 229 | "HTMLPurifier": "library/" 230 | }, 231 | "files": [ 232 | "library/HTMLPurifier.composer.php" 233 | ] 234 | }, 235 | "notification-url": "https://packagist.org/downloads/", 236 | "license": [ 237 | "LGPL" 238 | ], 239 | "authors": [ 240 | { 241 | "name": "Edward Z. Yang", 242 | "email": "admin@htmlpurifier.org", 243 | "homepage": "http://ezyang.com" 244 | } 245 | ], 246 | "description": "Standards compliant HTML filter written in PHP", 247 | "homepage": "http://htmlpurifier.org/", 248 | "keywords": [ 249 | "html" 250 | ], 251 | "time": "2016-07-16 12:58:58" 252 | }, 253 | { 254 | "name": "yiisoft/yii2", 255 | "version": "2.0.9", 256 | "source": { 257 | "type": "git", 258 | "url": "https://github.com/yiisoft/yii2-framework.git", 259 | "reference": "2b75151ea60e1fd820046416eee2e89c3dda1133" 260 | }, 261 | "dist": { 262 | "type": "zip", 263 | "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/2b75151ea60e1fd820046416eee2e89c3dda1133", 264 | "reference": "2b75151ea60e1fd820046416eee2e89c3dda1133", 265 | "shasum": "" 266 | }, 267 | "require": { 268 | "bower-asset/jquery": "2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", 269 | "bower-asset/jquery.inputmask": "~3.2.2", 270 | "bower-asset/punycode": "1.3.*", 271 | "bower-asset/yii2-pjax": "~2.0.1", 272 | "cebe/markdown": "~1.0.0 | ~1.1.0", 273 | "ext-ctype": "*", 274 | "ext-mbstring": "*", 275 | "ezyang/htmlpurifier": "~4.6", 276 | "lib-pcre": "*", 277 | "php": ">=5.4.0", 278 | "yiisoft/yii2-composer": "~2.0.4" 279 | }, 280 | "bin": [ 281 | "yii" 282 | ], 283 | "type": "library", 284 | "extra": { 285 | "branch-alias": { 286 | "dev-master": "2.0.x-dev" 287 | } 288 | }, 289 | "autoload": { 290 | "psr-4": { 291 | "yii\\": "" 292 | } 293 | }, 294 | "notification-url": "https://packagist.org/downloads/", 295 | "license": [ 296 | "BSD-3-Clause" 297 | ], 298 | "authors": [ 299 | { 300 | "name": "Qiang Xue", 301 | "email": "qiang.xue@gmail.com", 302 | "homepage": "http://www.yiiframework.com/", 303 | "role": "Founder and project lead" 304 | }, 305 | { 306 | "name": "Alexander Makarov", 307 | "email": "sam@rmcreative.ru", 308 | "homepage": "http://rmcreative.ru/", 309 | "role": "Core framework development" 310 | }, 311 | { 312 | "name": "Maurizio Domba", 313 | "homepage": "http://mdomba.info/", 314 | "role": "Core framework development" 315 | }, 316 | { 317 | "name": "Carsten Brandt", 318 | "email": "mail@cebe.cc", 319 | "homepage": "http://cebe.cc/", 320 | "role": "Core framework development" 321 | }, 322 | { 323 | "name": "Timur Ruziev", 324 | "email": "resurtm@gmail.com", 325 | "homepage": "http://resurtm.com/", 326 | "role": "Core framework development" 327 | }, 328 | { 329 | "name": "Paul Klimov", 330 | "email": "klimov.paul@gmail.com", 331 | "role": "Core framework development" 332 | }, 333 | { 334 | "name": "Dmitry Naumenko", 335 | "email": "d.naumenko.a@gmail.com", 336 | "role": "Core framework development" 337 | } 338 | ], 339 | "description": "Yii PHP Framework Version 2", 340 | "homepage": "http://www.yiiframework.com/", 341 | "keywords": [ 342 | "framework", 343 | "yii2" 344 | ], 345 | "time": "2016-07-11 13:36:42" 346 | }, 347 | { 348 | "name": "yiisoft/yii2-composer", 349 | "version": "2.0.4", 350 | "source": { 351 | "type": "git", 352 | "url": "https://github.com/yiisoft/yii2-composer.git", 353 | "reference": "7452fd908a5023b8bb5ea1b123a174ca080de464" 354 | }, 355 | "dist": { 356 | "type": "zip", 357 | "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/7452fd908a5023b8bb5ea1b123a174ca080de464", 358 | "reference": "7452fd908a5023b8bb5ea1b123a174ca080de464", 359 | "shasum": "" 360 | }, 361 | "require": { 362 | "composer-plugin-api": "^1.0" 363 | }, 364 | "type": "composer-plugin", 365 | "extra": { 366 | "class": "yii\\composer\\Plugin", 367 | "branch-alias": { 368 | "dev-master": "2.0.x-dev" 369 | } 370 | }, 371 | "autoload": { 372 | "psr-4": { 373 | "yii\\composer\\": "" 374 | } 375 | }, 376 | "notification-url": "https://packagist.org/downloads/", 377 | "license": [ 378 | "BSD-3-Clause" 379 | ], 380 | "authors": [ 381 | { 382 | "name": "Qiang Xue", 383 | "email": "qiang.xue@gmail.com" 384 | } 385 | ], 386 | "description": "The composer plugin for Yii extension installer", 387 | "keywords": [ 388 | "composer", 389 | "extension installer", 390 | "yii2" 391 | ], 392 | "time": "2016-02-06 00:49:24" 393 | } 394 | ], 395 | "packages-dev": [ 396 | { 397 | "name": "doctrine/instantiator", 398 | "version": "1.0.5", 399 | "source": { 400 | "type": "git", 401 | "url": "https://github.com/doctrine/instantiator.git", 402 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 403 | }, 404 | "dist": { 405 | "type": "zip", 406 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 407 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 408 | "shasum": "" 409 | }, 410 | "require": { 411 | "php": ">=5.3,<8.0-DEV" 412 | }, 413 | "require-dev": { 414 | "athletic/athletic": "~0.1.8", 415 | "ext-pdo": "*", 416 | "ext-phar": "*", 417 | "phpunit/phpunit": "~4.0", 418 | "squizlabs/php_codesniffer": "~2.0" 419 | }, 420 | "type": "library", 421 | "extra": { 422 | "branch-alias": { 423 | "dev-master": "1.0.x-dev" 424 | } 425 | }, 426 | "autoload": { 427 | "psr-4": { 428 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 429 | } 430 | }, 431 | "notification-url": "https://packagist.org/downloads/", 432 | "license": [ 433 | "MIT" 434 | ], 435 | "authors": [ 436 | { 437 | "name": "Marco Pivetta", 438 | "email": "ocramius@gmail.com", 439 | "homepage": "http://ocramius.github.com/" 440 | } 441 | ], 442 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 443 | "homepage": "https://github.com/doctrine/instantiator", 444 | "keywords": [ 445 | "constructor", 446 | "instantiate" 447 | ], 448 | "time": "2015-06-14 21:17:01" 449 | }, 450 | { 451 | "name": "phpdocumentor/reflection-common", 452 | "version": "1.0", 453 | "source": { 454 | "type": "git", 455 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 456 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" 457 | }, 458 | "dist": { 459 | "type": "zip", 460 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 461 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 462 | "shasum": "" 463 | }, 464 | "require": { 465 | "php": ">=5.5" 466 | }, 467 | "require-dev": { 468 | "phpunit/phpunit": "^4.6" 469 | }, 470 | "type": "library", 471 | "extra": { 472 | "branch-alias": { 473 | "dev-master": "1.0.x-dev" 474 | } 475 | }, 476 | "autoload": { 477 | "psr-4": { 478 | "phpDocumentor\\Reflection\\": [ 479 | "src" 480 | ] 481 | } 482 | }, 483 | "notification-url": "https://packagist.org/downloads/", 484 | "license": [ 485 | "MIT" 486 | ], 487 | "authors": [ 488 | { 489 | "name": "Jaap van Otterdijk", 490 | "email": "opensource@ijaap.nl" 491 | } 492 | ], 493 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 494 | "homepage": "http://www.phpdoc.org", 495 | "keywords": [ 496 | "FQSEN", 497 | "phpDocumentor", 498 | "phpdoc", 499 | "reflection", 500 | "static analysis" 501 | ], 502 | "time": "2015-12-27 11:43:31" 503 | }, 504 | { 505 | "name": "phpdocumentor/reflection-docblock", 506 | "version": "3.1.0", 507 | "source": { 508 | "type": "git", 509 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 510 | "reference": "9270140b940ff02e58ec577c237274e92cd40cdd" 511 | }, 512 | "dist": { 513 | "type": "zip", 514 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd", 515 | "reference": "9270140b940ff02e58ec577c237274e92cd40cdd", 516 | "shasum": "" 517 | }, 518 | "require": { 519 | "php": ">=5.5", 520 | "phpdocumentor/reflection-common": "^1.0@dev", 521 | "phpdocumentor/type-resolver": "^0.2.0", 522 | "webmozart/assert": "^1.0" 523 | }, 524 | "require-dev": { 525 | "mockery/mockery": "^0.9.4", 526 | "phpunit/phpunit": "^4.4" 527 | }, 528 | "type": "library", 529 | "autoload": { 530 | "psr-4": { 531 | "phpDocumentor\\Reflection\\": [ 532 | "src/" 533 | ] 534 | } 535 | }, 536 | "notification-url": "https://packagist.org/downloads/", 537 | "license": [ 538 | "MIT" 539 | ], 540 | "authors": [ 541 | { 542 | "name": "Mike van Riel", 543 | "email": "me@mikevanriel.com" 544 | } 545 | ], 546 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 547 | "time": "2016-06-10 09:48:41" 548 | }, 549 | { 550 | "name": "phpdocumentor/type-resolver", 551 | "version": "0.2", 552 | "source": { 553 | "type": "git", 554 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 555 | "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443" 556 | }, 557 | "dist": { 558 | "type": "zip", 559 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443", 560 | "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443", 561 | "shasum": "" 562 | }, 563 | "require": { 564 | "php": ">=5.5", 565 | "phpdocumentor/reflection-common": "^1.0" 566 | }, 567 | "require-dev": { 568 | "mockery/mockery": "^0.9.4", 569 | "phpunit/phpunit": "^5.2||^4.8.24" 570 | }, 571 | "type": "library", 572 | "extra": { 573 | "branch-alias": { 574 | "dev-master": "1.0.x-dev" 575 | } 576 | }, 577 | "autoload": { 578 | "psr-4": { 579 | "phpDocumentor\\Reflection\\": [ 580 | "src/" 581 | ] 582 | } 583 | }, 584 | "notification-url": "https://packagist.org/downloads/", 585 | "license": [ 586 | "MIT" 587 | ], 588 | "authors": [ 589 | { 590 | "name": "Mike van Riel", 591 | "email": "me@mikevanriel.com" 592 | } 593 | ], 594 | "time": "2016-06-10 07:14:17" 595 | }, 596 | { 597 | "name": "phpspec/prophecy", 598 | "version": "v1.6.1", 599 | "source": { 600 | "type": "git", 601 | "url": "https://github.com/phpspec/prophecy.git", 602 | "reference": "58a8137754bc24b25740d4281399a4a3596058e0" 603 | }, 604 | "dist": { 605 | "type": "zip", 606 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", 607 | "reference": "58a8137754bc24b25740d4281399a4a3596058e0", 608 | "shasum": "" 609 | }, 610 | "require": { 611 | "doctrine/instantiator": "^1.0.2", 612 | "php": "^5.3|^7.0", 613 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", 614 | "sebastian/comparator": "^1.1", 615 | "sebastian/recursion-context": "^1.0" 616 | }, 617 | "require-dev": { 618 | "phpspec/phpspec": "^2.0" 619 | }, 620 | "type": "library", 621 | "extra": { 622 | "branch-alias": { 623 | "dev-master": "1.6.x-dev" 624 | } 625 | }, 626 | "autoload": { 627 | "psr-0": { 628 | "Prophecy\\": "src/" 629 | } 630 | }, 631 | "notification-url": "https://packagist.org/downloads/", 632 | "license": [ 633 | "MIT" 634 | ], 635 | "authors": [ 636 | { 637 | "name": "Konstantin Kudryashov", 638 | "email": "ever.zet@gmail.com", 639 | "homepage": "http://everzet.com" 640 | }, 641 | { 642 | "name": "Marcello Duarte", 643 | "email": "marcello.duarte@gmail.com" 644 | } 645 | ], 646 | "description": "Highly opinionated mocking framework for PHP 5.3+", 647 | "homepage": "https://github.com/phpspec/prophecy", 648 | "keywords": [ 649 | "Double", 650 | "Dummy", 651 | "fake", 652 | "mock", 653 | "spy", 654 | "stub" 655 | ], 656 | "time": "2016-06-07 08:13:47" 657 | }, 658 | { 659 | "name": "phpunit/dbunit", 660 | "version": "1.4.1", 661 | "source": { 662 | "type": "git", 663 | "url": "https://github.com/sebastianbergmann/dbunit.git", 664 | "reference": "9aaee6447663ff1b0cd50c23637e04af74c5e2ae" 665 | }, 666 | "dist": { 667 | "type": "zip", 668 | "url": "https://api.github.com/repos/sebastianbergmann/dbunit/zipball/9aaee6447663ff1b0cd50c23637e04af74c5e2ae", 669 | "reference": "9aaee6447663ff1b0cd50c23637e04af74c5e2ae", 670 | "shasum": "" 671 | }, 672 | "require": { 673 | "ext-pdo": "*", 674 | "ext-simplexml": "*", 675 | "php": ">=5.3.3", 676 | "phpunit/phpunit": "~4|~5", 677 | "symfony/yaml": "~2.1|~3.0" 678 | }, 679 | "bin": [ 680 | "composer/bin/dbunit" 681 | ], 682 | "type": "library", 683 | "extra": { 684 | "branch-alias": { 685 | "dev-master": "1.3.x-dev" 686 | } 687 | }, 688 | "autoload": { 689 | "classmap": [ 690 | "PHPUnit/" 691 | ] 692 | }, 693 | "notification-url": "https://packagist.org/downloads/", 694 | "include-path": [ 695 | "", 696 | "../../symfony/yaml/" 697 | ], 698 | "license": [ 699 | "BSD-3-Clause" 700 | ], 701 | "authors": [ 702 | { 703 | "name": "Sebastian Bergmann", 704 | "email": "sb@sebastian-bergmann.de", 705 | "role": "lead" 706 | } 707 | ], 708 | "description": "DbUnit port for PHP/PHPUnit to support database interaction testing.", 709 | "homepage": "https://github.com/sebastianbergmann/dbunit/", 710 | "keywords": [ 711 | "database", 712 | "testing", 713 | "xunit" 714 | ], 715 | "time": "2015-08-07 04:57:38" 716 | }, 717 | { 718 | "name": "phpunit/php-code-coverage", 719 | "version": "2.2.4", 720 | "source": { 721 | "type": "git", 722 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 723 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" 724 | }, 725 | "dist": { 726 | "type": "zip", 727 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", 728 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", 729 | "shasum": "" 730 | }, 731 | "require": { 732 | "php": ">=5.3.3", 733 | "phpunit/php-file-iterator": "~1.3", 734 | "phpunit/php-text-template": "~1.2", 735 | "phpunit/php-token-stream": "~1.3", 736 | "sebastian/environment": "^1.3.2", 737 | "sebastian/version": "~1.0" 738 | }, 739 | "require-dev": { 740 | "ext-xdebug": ">=2.1.4", 741 | "phpunit/phpunit": "~4" 742 | }, 743 | "suggest": { 744 | "ext-dom": "*", 745 | "ext-xdebug": ">=2.2.1", 746 | "ext-xmlwriter": "*" 747 | }, 748 | "type": "library", 749 | "extra": { 750 | "branch-alias": { 751 | "dev-master": "2.2.x-dev" 752 | } 753 | }, 754 | "autoload": { 755 | "classmap": [ 756 | "src/" 757 | ] 758 | }, 759 | "notification-url": "https://packagist.org/downloads/", 760 | "license": [ 761 | "BSD-3-Clause" 762 | ], 763 | "authors": [ 764 | { 765 | "name": "Sebastian Bergmann", 766 | "email": "sb@sebastian-bergmann.de", 767 | "role": "lead" 768 | } 769 | ], 770 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 771 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 772 | "keywords": [ 773 | "coverage", 774 | "testing", 775 | "xunit" 776 | ], 777 | "time": "2015-10-06 15:47:00" 778 | }, 779 | { 780 | "name": "phpunit/php-file-iterator", 781 | "version": "1.4.1", 782 | "source": { 783 | "type": "git", 784 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 785 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" 786 | }, 787 | "dist": { 788 | "type": "zip", 789 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 790 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 791 | "shasum": "" 792 | }, 793 | "require": { 794 | "php": ">=5.3.3" 795 | }, 796 | "type": "library", 797 | "extra": { 798 | "branch-alias": { 799 | "dev-master": "1.4.x-dev" 800 | } 801 | }, 802 | "autoload": { 803 | "classmap": [ 804 | "src/" 805 | ] 806 | }, 807 | "notification-url": "https://packagist.org/downloads/", 808 | "license": [ 809 | "BSD-3-Clause" 810 | ], 811 | "authors": [ 812 | { 813 | "name": "Sebastian Bergmann", 814 | "email": "sb@sebastian-bergmann.de", 815 | "role": "lead" 816 | } 817 | ], 818 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 819 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 820 | "keywords": [ 821 | "filesystem", 822 | "iterator" 823 | ], 824 | "time": "2015-06-21 13:08:43" 825 | }, 826 | { 827 | "name": "phpunit/php-text-template", 828 | "version": "1.2.1", 829 | "source": { 830 | "type": "git", 831 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 832 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 833 | }, 834 | "dist": { 835 | "type": "zip", 836 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 837 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 838 | "shasum": "" 839 | }, 840 | "require": { 841 | "php": ">=5.3.3" 842 | }, 843 | "type": "library", 844 | "autoload": { 845 | "classmap": [ 846 | "src/" 847 | ] 848 | }, 849 | "notification-url": "https://packagist.org/downloads/", 850 | "license": [ 851 | "BSD-3-Clause" 852 | ], 853 | "authors": [ 854 | { 855 | "name": "Sebastian Bergmann", 856 | "email": "sebastian@phpunit.de", 857 | "role": "lead" 858 | } 859 | ], 860 | "description": "Simple template engine.", 861 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 862 | "keywords": [ 863 | "template" 864 | ], 865 | "time": "2015-06-21 13:50:34" 866 | }, 867 | { 868 | "name": "phpunit/php-timer", 869 | "version": "1.0.8", 870 | "source": { 871 | "type": "git", 872 | "url": "https://github.com/sebastianbergmann/php-timer.git", 873 | "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" 874 | }, 875 | "dist": { 876 | "type": "zip", 877 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", 878 | "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", 879 | "shasum": "" 880 | }, 881 | "require": { 882 | "php": ">=5.3.3" 883 | }, 884 | "require-dev": { 885 | "phpunit/phpunit": "~4|~5" 886 | }, 887 | "type": "library", 888 | "autoload": { 889 | "classmap": [ 890 | "src/" 891 | ] 892 | }, 893 | "notification-url": "https://packagist.org/downloads/", 894 | "license": [ 895 | "BSD-3-Clause" 896 | ], 897 | "authors": [ 898 | { 899 | "name": "Sebastian Bergmann", 900 | "email": "sb@sebastian-bergmann.de", 901 | "role": "lead" 902 | } 903 | ], 904 | "description": "Utility class for timing", 905 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 906 | "keywords": [ 907 | "timer" 908 | ], 909 | "time": "2016-05-12 18:03:57" 910 | }, 911 | { 912 | "name": "phpunit/php-token-stream", 913 | "version": "1.4.8", 914 | "source": { 915 | "type": "git", 916 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 917 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" 918 | }, 919 | "dist": { 920 | "type": "zip", 921 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 922 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 923 | "shasum": "" 924 | }, 925 | "require": { 926 | "ext-tokenizer": "*", 927 | "php": ">=5.3.3" 928 | }, 929 | "require-dev": { 930 | "phpunit/phpunit": "~4.2" 931 | }, 932 | "type": "library", 933 | "extra": { 934 | "branch-alias": { 935 | "dev-master": "1.4-dev" 936 | } 937 | }, 938 | "autoload": { 939 | "classmap": [ 940 | "src/" 941 | ] 942 | }, 943 | "notification-url": "https://packagist.org/downloads/", 944 | "license": [ 945 | "BSD-3-Clause" 946 | ], 947 | "authors": [ 948 | { 949 | "name": "Sebastian Bergmann", 950 | "email": "sebastian@phpunit.de" 951 | } 952 | ], 953 | "description": "Wrapper around PHP's tokenizer extension.", 954 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 955 | "keywords": [ 956 | "tokenizer" 957 | ], 958 | "time": "2015-09-15 10:49:45" 959 | }, 960 | { 961 | "name": "phpunit/phpunit", 962 | "version": "4.8.27", 963 | "source": { 964 | "type": "git", 965 | "url": "https://github.com/sebastianbergmann/phpunit.git", 966 | "reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90" 967 | }, 968 | "dist": { 969 | "type": "zip", 970 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c062dddcb68e44b563f66ee319ddae2b5a322a90", 971 | "reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90", 972 | "shasum": "" 973 | }, 974 | "require": { 975 | "ext-dom": "*", 976 | "ext-json": "*", 977 | "ext-pcre": "*", 978 | "ext-reflection": "*", 979 | "ext-spl": "*", 980 | "php": ">=5.3.3", 981 | "phpspec/prophecy": "^1.3.1", 982 | "phpunit/php-code-coverage": "~2.1", 983 | "phpunit/php-file-iterator": "~1.4", 984 | "phpunit/php-text-template": "~1.2", 985 | "phpunit/php-timer": "^1.0.6", 986 | "phpunit/phpunit-mock-objects": "~2.3", 987 | "sebastian/comparator": "~1.1", 988 | "sebastian/diff": "~1.2", 989 | "sebastian/environment": "~1.3", 990 | "sebastian/exporter": "~1.2", 991 | "sebastian/global-state": "~1.0", 992 | "sebastian/version": "~1.0", 993 | "symfony/yaml": "~2.1|~3.0" 994 | }, 995 | "suggest": { 996 | "phpunit/php-invoker": "~1.1" 997 | }, 998 | "bin": [ 999 | "phpunit" 1000 | ], 1001 | "type": "library", 1002 | "extra": { 1003 | "branch-alias": { 1004 | "dev-master": "4.8.x-dev" 1005 | } 1006 | }, 1007 | "autoload": { 1008 | "classmap": [ 1009 | "src/" 1010 | ] 1011 | }, 1012 | "notification-url": "https://packagist.org/downloads/", 1013 | "license": [ 1014 | "BSD-3-Clause" 1015 | ], 1016 | "authors": [ 1017 | { 1018 | "name": "Sebastian Bergmann", 1019 | "email": "sebastian@phpunit.de", 1020 | "role": "lead" 1021 | } 1022 | ], 1023 | "description": "The PHP Unit Testing framework.", 1024 | "homepage": "https://phpunit.de/", 1025 | "keywords": [ 1026 | "phpunit", 1027 | "testing", 1028 | "xunit" 1029 | ], 1030 | "time": "2016-07-21 06:48:14" 1031 | }, 1032 | { 1033 | "name": "phpunit/phpunit-mock-objects", 1034 | "version": "2.3.8", 1035 | "source": { 1036 | "type": "git", 1037 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 1038 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" 1039 | }, 1040 | "dist": { 1041 | "type": "zip", 1042 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", 1043 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", 1044 | "shasum": "" 1045 | }, 1046 | "require": { 1047 | "doctrine/instantiator": "^1.0.2", 1048 | "php": ">=5.3.3", 1049 | "phpunit/php-text-template": "~1.2", 1050 | "sebastian/exporter": "~1.2" 1051 | }, 1052 | "require-dev": { 1053 | "phpunit/phpunit": "~4.4" 1054 | }, 1055 | "suggest": { 1056 | "ext-soap": "*" 1057 | }, 1058 | "type": "library", 1059 | "extra": { 1060 | "branch-alias": { 1061 | "dev-master": "2.3.x-dev" 1062 | } 1063 | }, 1064 | "autoload": { 1065 | "classmap": [ 1066 | "src/" 1067 | ] 1068 | }, 1069 | "notification-url": "https://packagist.org/downloads/", 1070 | "license": [ 1071 | "BSD-3-Clause" 1072 | ], 1073 | "authors": [ 1074 | { 1075 | "name": "Sebastian Bergmann", 1076 | "email": "sb@sebastian-bergmann.de", 1077 | "role": "lead" 1078 | } 1079 | ], 1080 | "description": "Mock Object library for PHPUnit", 1081 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 1082 | "keywords": [ 1083 | "mock", 1084 | "xunit" 1085 | ], 1086 | "time": "2015-10-02 06:51:40" 1087 | }, 1088 | { 1089 | "name": "sebastian/comparator", 1090 | "version": "1.2.0", 1091 | "source": { 1092 | "type": "git", 1093 | "url": "https://github.com/sebastianbergmann/comparator.git", 1094 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" 1095 | }, 1096 | "dist": { 1097 | "type": "zip", 1098 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", 1099 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", 1100 | "shasum": "" 1101 | }, 1102 | "require": { 1103 | "php": ">=5.3.3", 1104 | "sebastian/diff": "~1.2", 1105 | "sebastian/exporter": "~1.2" 1106 | }, 1107 | "require-dev": { 1108 | "phpunit/phpunit": "~4.4" 1109 | }, 1110 | "type": "library", 1111 | "extra": { 1112 | "branch-alias": { 1113 | "dev-master": "1.2.x-dev" 1114 | } 1115 | }, 1116 | "autoload": { 1117 | "classmap": [ 1118 | "src/" 1119 | ] 1120 | }, 1121 | "notification-url": "https://packagist.org/downloads/", 1122 | "license": [ 1123 | "BSD-3-Clause" 1124 | ], 1125 | "authors": [ 1126 | { 1127 | "name": "Jeff Welch", 1128 | "email": "whatthejeff@gmail.com" 1129 | }, 1130 | { 1131 | "name": "Volker Dusch", 1132 | "email": "github@wallbash.com" 1133 | }, 1134 | { 1135 | "name": "Bernhard Schussek", 1136 | "email": "bschussek@2bepublished.at" 1137 | }, 1138 | { 1139 | "name": "Sebastian Bergmann", 1140 | "email": "sebastian@phpunit.de" 1141 | } 1142 | ], 1143 | "description": "Provides the functionality to compare PHP values for equality", 1144 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 1145 | "keywords": [ 1146 | "comparator", 1147 | "compare", 1148 | "equality" 1149 | ], 1150 | "time": "2015-07-26 15:48:44" 1151 | }, 1152 | { 1153 | "name": "sebastian/diff", 1154 | "version": "1.4.1", 1155 | "source": { 1156 | "type": "git", 1157 | "url": "https://github.com/sebastianbergmann/diff.git", 1158 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" 1159 | }, 1160 | "dist": { 1161 | "type": "zip", 1162 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", 1163 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", 1164 | "shasum": "" 1165 | }, 1166 | "require": { 1167 | "php": ">=5.3.3" 1168 | }, 1169 | "require-dev": { 1170 | "phpunit/phpunit": "~4.8" 1171 | }, 1172 | "type": "library", 1173 | "extra": { 1174 | "branch-alias": { 1175 | "dev-master": "1.4-dev" 1176 | } 1177 | }, 1178 | "autoload": { 1179 | "classmap": [ 1180 | "src/" 1181 | ] 1182 | }, 1183 | "notification-url": "https://packagist.org/downloads/", 1184 | "license": [ 1185 | "BSD-3-Clause" 1186 | ], 1187 | "authors": [ 1188 | { 1189 | "name": "Kore Nordmann", 1190 | "email": "mail@kore-nordmann.de" 1191 | }, 1192 | { 1193 | "name": "Sebastian Bergmann", 1194 | "email": "sebastian@phpunit.de" 1195 | } 1196 | ], 1197 | "description": "Diff implementation", 1198 | "homepage": "https://github.com/sebastianbergmann/diff", 1199 | "keywords": [ 1200 | "diff" 1201 | ], 1202 | "time": "2015-12-08 07:14:41" 1203 | }, 1204 | { 1205 | "name": "sebastian/environment", 1206 | "version": "1.3.7", 1207 | "source": { 1208 | "type": "git", 1209 | "url": "https://github.com/sebastianbergmann/environment.git", 1210 | "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716" 1211 | }, 1212 | "dist": { 1213 | "type": "zip", 1214 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716", 1215 | "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716", 1216 | "shasum": "" 1217 | }, 1218 | "require": { 1219 | "php": ">=5.3.3" 1220 | }, 1221 | "require-dev": { 1222 | "phpunit/phpunit": "~4.4" 1223 | }, 1224 | "type": "library", 1225 | "extra": { 1226 | "branch-alias": { 1227 | "dev-master": "1.3.x-dev" 1228 | } 1229 | }, 1230 | "autoload": { 1231 | "classmap": [ 1232 | "src/" 1233 | ] 1234 | }, 1235 | "notification-url": "https://packagist.org/downloads/", 1236 | "license": [ 1237 | "BSD-3-Clause" 1238 | ], 1239 | "authors": [ 1240 | { 1241 | "name": "Sebastian Bergmann", 1242 | "email": "sebastian@phpunit.de" 1243 | } 1244 | ], 1245 | "description": "Provides functionality to handle HHVM/PHP environments", 1246 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1247 | "keywords": [ 1248 | "Xdebug", 1249 | "environment", 1250 | "hhvm" 1251 | ], 1252 | "time": "2016-05-17 03:18:57" 1253 | }, 1254 | { 1255 | "name": "sebastian/exporter", 1256 | "version": "1.2.2", 1257 | "source": { 1258 | "type": "git", 1259 | "url": "https://github.com/sebastianbergmann/exporter.git", 1260 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" 1261 | }, 1262 | "dist": { 1263 | "type": "zip", 1264 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", 1265 | "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", 1266 | "shasum": "" 1267 | }, 1268 | "require": { 1269 | "php": ">=5.3.3", 1270 | "sebastian/recursion-context": "~1.0" 1271 | }, 1272 | "require-dev": { 1273 | "ext-mbstring": "*", 1274 | "phpunit/phpunit": "~4.4" 1275 | }, 1276 | "type": "library", 1277 | "extra": { 1278 | "branch-alias": { 1279 | "dev-master": "1.3.x-dev" 1280 | } 1281 | }, 1282 | "autoload": { 1283 | "classmap": [ 1284 | "src/" 1285 | ] 1286 | }, 1287 | "notification-url": "https://packagist.org/downloads/", 1288 | "license": [ 1289 | "BSD-3-Clause" 1290 | ], 1291 | "authors": [ 1292 | { 1293 | "name": "Jeff Welch", 1294 | "email": "whatthejeff@gmail.com" 1295 | }, 1296 | { 1297 | "name": "Volker Dusch", 1298 | "email": "github@wallbash.com" 1299 | }, 1300 | { 1301 | "name": "Bernhard Schussek", 1302 | "email": "bschussek@2bepublished.at" 1303 | }, 1304 | { 1305 | "name": "Sebastian Bergmann", 1306 | "email": "sebastian@phpunit.de" 1307 | }, 1308 | { 1309 | "name": "Adam Harvey", 1310 | "email": "aharvey@php.net" 1311 | } 1312 | ], 1313 | "description": "Provides the functionality to export PHP variables for visualization", 1314 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1315 | "keywords": [ 1316 | "export", 1317 | "exporter" 1318 | ], 1319 | "time": "2016-06-17 09:04:28" 1320 | }, 1321 | { 1322 | "name": "sebastian/global-state", 1323 | "version": "1.1.1", 1324 | "source": { 1325 | "type": "git", 1326 | "url": "https://github.com/sebastianbergmann/global-state.git", 1327 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 1328 | }, 1329 | "dist": { 1330 | "type": "zip", 1331 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 1332 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 1333 | "shasum": "" 1334 | }, 1335 | "require": { 1336 | "php": ">=5.3.3" 1337 | }, 1338 | "require-dev": { 1339 | "phpunit/phpunit": "~4.2" 1340 | }, 1341 | "suggest": { 1342 | "ext-uopz": "*" 1343 | }, 1344 | "type": "library", 1345 | "extra": { 1346 | "branch-alias": { 1347 | "dev-master": "1.0-dev" 1348 | } 1349 | }, 1350 | "autoload": { 1351 | "classmap": [ 1352 | "src/" 1353 | ] 1354 | }, 1355 | "notification-url": "https://packagist.org/downloads/", 1356 | "license": [ 1357 | "BSD-3-Clause" 1358 | ], 1359 | "authors": [ 1360 | { 1361 | "name": "Sebastian Bergmann", 1362 | "email": "sebastian@phpunit.de" 1363 | } 1364 | ], 1365 | "description": "Snapshotting of global state", 1366 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1367 | "keywords": [ 1368 | "global state" 1369 | ], 1370 | "time": "2015-10-12 03:26:01" 1371 | }, 1372 | { 1373 | "name": "sebastian/recursion-context", 1374 | "version": "1.0.2", 1375 | "source": { 1376 | "type": "git", 1377 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1378 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791" 1379 | }, 1380 | "dist": { 1381 | "type": "zip", 1382 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", 1383 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791", 1384 | "shasum": "" 1385 | }, 1386 | "require": { 1387 | "php": ">=5.3.3" 1388 | }, 1389 | "require-dev": { 1390 | "phpunit/phpunit": "~4.4" 1391 | }, 1392 | "type": "library", 1393 | "extra": { 1394 | "branch-alias": { 1395 | "dev-master": "1.0.x-dev" 1396 | } 1397 | }, 1398 | "autoload": { 1399 | "classmap": [ 1400 | "src/" 1401 | ] 1402 | }, 1403 | "notification-url": "https://packagist.org/downloads/", 1404 | "license": [ 1405 | "BSD-3-Clause" 1406 | ], 1407 | "authors": [ 1408 | { 1409 | "name": "Jeff Welch", 1410 | "email": "whatthejeff@gmail.com" 1411 | }, 1412 | { 1413 | "name": "Sebastian Bergmann", 1414 | "email": "sebastian@phpunit.de" 1415 | }, 1416 | { 1417 | "name": "Adam Harvey", 1418 | "email": "aharvey@php.net" 1419 | } 1420 | ], 1421 | "description": "Provides functionality to recursively process PHP variables", 1422 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1423 | "time": "2015-11-11 19:50:13" 1424 | }, 1425 | { 1426 | "name": "sebastian/version", 1427 | "version": "1.0.6", 1428 | "source": { 1429 | "type": "git", 1430 | "url": "https://github.com/sebastianbergmann/version.git", 1431 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 1432 | }, 1433 | "dist": { 1434 | "type": "zip", 1435 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1436 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1437 | "shasum": "" 1438 | }, 1439 | "type": "library", 1440 | "autoload": { 1441 | "classmap": [ 1442 | "src/" 1443 | ] 1444 | }, 1445 | "notification-url": "https://packagist.org/downloads/", 1446 | "license": [ 1447 | "BSD-3-Clause" 1448 | ], 1449 | "authors": [ 1450 | { 1451 | "name": "Sebastian Bergmann", 1452 | "email": "sebastian@phpunit.de", 1453 | "role": "lead" 1454 | } 1455 | ], 1456 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1457 | "homepage": "https://github.com/sebastianbergmann/version", 1458 | "time": "2015-06-21 13:59:46" 1459 | }, 1460 | { 1461 | "name": "symfony/yaml", 1462 | "version": "v3.1.3", 1463 | "source": { 1464 | "type": "git", 1465 | "url": "https://github.com/symfony/yaml.git", 1466 | "reference": "1819adf2066880c7967df7180f4f662b6f0567ac" 1467 | }, 1468 | "dist": { 1469 | "type": "zip", 1470 | "url": "https://api.github.com/repos/symfony/yaml/zipball/1819adf2066880c7967df7180f4f662b6f0567ac", 1471 | "reference": "1819adf2066880c7967df7180f4f662b6f0567ac", 1472 | "shasum": "" 1473 | }, 1474 | "require": { 1475 | "php": ">=5.5.9" 1476 | }, 1477 | "type": "library", 1478 | "extra": { 1479 | "branch-alias": { 1480 | "dev-master": "3.1-dev" 1481 | } 1482 | }, 1483 | "autoload": { 1484 | "psr-4": { 1485 | "Symfony\\Component\\Yaml\\": "" 1486 | }, 1487 | "exclude-from-classmap": [ 1488 | "/Tests/" 1489 | ] 1490 | }, 1491 | "notification-url": "https://packagist.org/downloads/", 1492 | "license": [ 1493 | "MIT" 1494 | ], 1495 | "authors": [ 1496 | { 1497 | "name": "Fabien Potencier", 1498 | "email": "fabien@symfony.com" 1499 | }, 1500 | { 1501 | "name": "Symfony Community", 1502 | "homepage": "https://symfony.com/contributors" 1503 | } 1504 | ], 1505 | "description": "Symfony Yaml Component", 1506 | "homepage": "https://symfony.com", 1507 | "time": "2016-07-17 14:02:08" 1508 | }, 1509 | { 1510 | "name": "webmozart/assert", 1511 | "version": "1.0.2", 1512 | "source": { 1513 | "type": "git", 1514 | "url": "https://github.com/webmozart/assert.git", 1515 | "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde" 1516 | }, 1517 | "dist": { 1518 | "type": "zip", 1519 | "url": "https://api.github.com/repos/webmozart/assert/zipball/30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", 1520 | "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", 1521 | "shasum": "" 1522 | }, 1523 | "require": { 1524 | "php": ">=5.3.3" 1525 | }, 1526 | "require-dev": { 1527 | "phpunit/phpunit": "^4.6" 1528 | }, 1529 | "type": "library", 1530 | "extra": { 1531 | "branch-alias": { 1532 | "dev-master": "1.0-dev" 1533 | } 1534 | }, 1535 | "autoload": { 1536 | "psr-4": { 1537 | "Webmozart\\Assert\\": "src/" 1538 | } 1539 | }, 1540 | "notification-url": "https://packagist.org/downloads/", 1541 | "license": [ 1542 | "MIT" 1543 | ], 1544 | "authors": [ 1545 | { 1546 | "name": "Bernhard Schussek", 1547 | "email": "bschussek@gmail.com" 1548 | } 1549 | ], 1550 | "description": "Assertions to validate method input/output with nice error messages.", 1551 | "keywords": [ 1552 | "assert", 1553 | "check", 1554 | "validate" 1555 | ], 1556 | "time": "2015-08-24 13:29:44" 1557 | } 1558 | ], 1559 | "aliases": [], 1560 | "minimum-stability": "stable", 1561 | "stability-flags": [], 1562 | "prefer-stable": false, 1563 | "prefer-lowest": false, 1564 | "platform": { 1565 | "php": ">=5.4.0" 1566 | }, 1567 | "platform-dev": [] 1568 | } 1569 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | ./tests 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | /data/config.local.php -------------------------------------------------------------------------------- /tests/ArrayDataSet.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable\tests; 9 | 10 | /** 11 | * @author PaulZi 12 | */ 13 | class ArrayDataSet extends \PHPUnit_Extensions_Database_DataSet_ArrayDataSet 14 | { 15 | public function __construct(array $data) 16 | { 17 | foreach ($data as $tableName => $rows) { 18 | $columns = array(); 19 | if (isset($rows['columns'])) { 20 | $columns = $rows['columns']; 21 | unset($rows['columns']); 22 | } elseif (isset($rows[0])) { 23 | $columns = array_keys($rows[0]); 24 | } 25 | 26 | $metaData = new \PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tableName, $columns); 27 | $table = new \PHPUnit_Extensions_Database_DataSet_DefaultTable($metaData); 28 | 29 | foreach ($rows AS $row) { 30 | $table->addRow($row); 31 | } 32 | $this->tables[$tableName] = $table; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /tests/BaseTestCase.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable\tests; 9 | 10 | use paulzi\sortable\tests\migrations\TestMigration; 11 | use Yii; 12 | use yii\db\Connection; 13 | 14 | /** 15 | * @author PaulZi 16 | */ 17 | class BaseTestCase extends \PHPUnit_Extensions_Database_TestCase 18 | { 19 | protected static $driverName = 'sqlite'; 20 | 21 | 22 | /** 23 | * @inheritdoc 24 | */ 25 | public function getConnection() 26 | { 27 | return $this->createDefaultDBConnection(Yii::$app->getDb()->pdo); 28 | } 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public function getDataSet() 34 | { 35 | return new \PHPUnit_Extensions_Database_DataSet_ArrayDataSet(require(__DIR__ . '/data/data.php')); 36 | } 37 | 38 | /** 39 | * @inheritdoc 40 | */ 41 | protected function setUp() 42 | { 43 | if (Yii::$app->get('db', false) === null) { 44 | $this->markTestSkipped(); 45 | } else { 46 | (new TestMigration())->up(); 47 | if (Yii::$app->db->driverName === 'pgsql') { 48 | Yii::$app->db->createCommand("SELECT setval(pg_get_serial_sequence('item', 'id'), 14)")->execute(); 49 | } 50 | parent::setUp(); 51 | } 52 | } 53 | 54 | /** 55 | * @inheritdoc 56 | */ 57 | public static function setUpBeforeClass() 58 | { 59 | $config = require(__DIR__ . '/data/config.php'); 60 | $config = $config[static::$driverName]; 61 | $config['class'] = Connection::className(); 62 | try { 63 | Yii::$app->set('db', $config); 64 | Yii::$app->getDb()->open(); 65 | } catch (\Exception $e) { 66 | Yii::$app->clear('db'); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Yii2 Sortable Behavior unit tests 2 | 3 | ## How to run the test 4 | 5 | Make sure you have PHPUnit installed and that you installed all composer dependencies (run `composer update` in the repo base directory). 6 | 7 | Run PHPUnit in the yii repo base directory. 8 | 9 | ``` 10 | phpunit 11 | ``` 12 | 13 | You can run tests for specific groups only: 14 | 15 | ``` 16 | phpunit --group=sqlite,mysql 17 | ``` 18 | 19 | You can get a list of available groups via `phpunit --list-groups`. 20 | 21 | ## test configurations 22 | 23 | PHPUnit configuration is in `phpunit.xml.dist` in repository root folder. 24 | You can create your own phpunit.xml to override dist config. 25 | 26 | Database and other backend system configuration can be found in `tests/data/config.php` 27 | adjust them to your needs to allow testing databases and caching in your environment. 28 | You can override configuration values by creating a `config.local.php` file 29 | and manipulate the `$config` variable. 30 | For example to change MySQL username and password your `config.local.php` should 31 | contain the following: 32 | 33 | ```php 34 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable\tests; 9 | 10 | use paulzi\sortable\tests\models\Item; 11 | use paulzi\sortable\tests\models\ItemWindow; 12 | use Yii; 13 | 14 | /** 15 | * @author PaulZi 16 | */ 17 | class SortableBehaviorTestCase extends BaseTestCase 18 | { 19 | public function testGetSortablePosition() 20 | { 21 | $this->assertEquals(-1, Item::findOne(9)->getSortablePosition()); 22 | $this->assertEquals(-1, ItemWindow::findOne(9)->getSortablePosition()); 23 | } 24 | 25 | public function testMoveFirstInsertInNoEmpty() 26 | { 27 | $item = new Item(['parent_id' => 1, 'slug' => 'new1']); 28 | $this->assertTrue($item->moveFirst()->save()); 29 | 30 | $item = new ItemWindow(['parent_id' => 6, 'slug' => 'new2']); 31 | $this->assertTrue($item->moveFirst()->save()); 32 | 33 | $dataSet = $this->getConnection()->createDataSet(['item']); 34 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-first-insert-in-no-empty.php')); 35 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 36 | } 37 | 38 | public function testMoveFirstInsertInEmpty() 39 | { 40 | $item = new Item(['parent_id' => 4, 'slug' => 'new1']); 41 | $this->assertTrue($item->moveFirst()->save()); 42 | 43 | $item = new ItemWindow(['parent_id' => 11, 'slug' => 'new2']); 44 | $this->assertTrue($item->moveFirst()->save()); 45 | 46 | $dataSet = $this->getConnection()->createDataSet(['item']); 47 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-first-insert-in-empty.php')); 48 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 49 | } 50 | 51 | public function testMoveFirstUpdateSameScope() 52 | { 53 | $item = Item::findOne(2); 54 | $this->assertTrue($item->moveFirst()->save()); 55 | 56 | $item = ItemWindow::findOne(10); 57 | $this->assertTrue($item->moveFirst()->save()); 58 | 59 | $dataSet = $this->getConnection()->createDataSet(['item']); 60 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-first-update-same-scope.php')); 61 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 62 | } 63 | 64 | public function testMoveFirstUpdateOtherScope() 65 | { 66 | $item = Item::findOne(3); 67 | $item->parent_id = 6; 68 | $this->assertTrue($item->moveFirst()->save()); 69 | 70 | $item = ItemWindow::findOne(12); 71 | $item->parent_id = 1; 72 | $this->assertTrue($item->moveFirst()->save()); 73 | 74 | $dataSet = $this->getConnection()->createDataSet(['item']); 75 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-first-update-other-scope.php')); 76 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 77 | } 78 | 79 | public function testMoveFirstUpdateSelf() 80 | { 81 | $item = Item::findOne(4); 82 | $this->assertTrue($item->moveFirst()->save()); 83 | 84 | $item = ItemWindow::findOne(7); 85 | $this->assertTrue($item->moveFirst()->save()); 86 | 87 | $dataSet = $this->getConnection()->createDataSet(['item']); 88 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/data.php')); 89 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 90 | } 91 | 92 | public function testMoveLastInsertInNoEmpty() 93 | { 94 | $item = new Item(['parent_id' => 1, 'slug' => 'new1']); 95 | $this->assertTrue($item->moveLast()->save()); 96 | 97 | $item = new ItemWindow(['parent_id' => 6, 'slug' => 'new2']); 98 | $this->assertTrue($item->moveLast()->save()); 99 | 100 | $dataSet = $this->getConnection()->createDataSet(['item']); 101 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-last-insert-in-no-empty.php')); 102 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 103 | } 104 | 105 | public function testMoveLastInsertInEmpty() 106 | { 107 | $item = new Item(['parent_id' => 4, 'slug' => 'new1']); 108 | $this->assertTrue($item->moveLast()->save()); 109 | 110 | $item = new ItemWindow(['parent_id' => 11, 'slug' => 'new2']); 111 | $this->assertTrue($item->moveLast()->save()); 112 | 113 | $dataSet = $this->getConnection()->createDataSet(['item']); 114 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-last-insert-in-empty.php')); 115 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 116 | } 117 | 118 | public function testMoveLastUpdateSameScope() 119 | { 120 | $item = Item::findOne(3); 121 | $this->assertTrue($item->moveLast()->save()); 122 | 123 | $item = ItemWindow::findOne(10); 124 | $this->assertTrue($item->moveLast()->save()); 125 | 126 | $dataSet = $this->getConnection()->createDataSet(['item']); 127 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-last-update-same-scope.php')); 128 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 129 | } 130 | 131 | public function testMoveLastUpdateOtherScope() 132 | { 133 | $item = Item::findOne(3); 134 | $item->parent_id = 6; 135 | $this->assertTrue($item->moveLast()->save()); 136 | 137 | $item = ItemWindow::findOne(12); 138 | $item->parent_id = 1; 139 | $this->assertTrue($item->moveLast()->save()); 140 | 141 | $dataSet = $this->getConnection()->createDataSet(['item']); 142 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-last-update-other-scope.php')); 143 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 144 | } 145 | 146 | public function testMoveLastUpdateSelf() 147 | { 148 | $item = Item::findOne(5); 149 | $this->assertTrue($item->moveLast()->save()); 150 | 151 | $item = ItemWindow::findOne(8); 152 | $this->assertTrue($item->moveLast()->save()); 153 | 154 | $dataSet = $this->getConnection()->createDataSet(['item']); 155 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/data.php')); 156 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 157 | } 158 | 159 | public function testMoveToInsertNoGap() 160 | { 161 | $item = new Item(['parent_id' => 1, 'slug' => 'new1']); 162 | $this->assertTrue($item->moveTo(0, true)->save()); 163 | 164 | $item = new ItemWindow(['parent_id' => 6, 'slug' => 'new2']); 165 | $this->assertTrue($item->moveTo(3, false)->save()); 166 | 167 | $dataSet = $this->getConnection()->createDataSet(['item']); 168 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-to-insert-no-gap.php')); 169 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 170 | } 171 | 172 | public function testMoveToInsertGap() 173 | { 174 | $item = new Item(['parent_id' => 1, 'slug' => 'new1']); 175 | $this->assertTrue($item->moveTo(-2, true)->save()); 176 | 177 | $item = new ItemWindow(['parent_id' => 6, 'slug' => 'new2']); 178 | $this->assertTrue($item->moveTo(4, false)->save()); 179 | 180 | $dataSet = $this->getConnection()->createDataSet(['item']); 181 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-to-insert-gap.php')); 182 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 183 | } 184 | 185 | public function testMoveToUpdateSameScope() 186 | { 187 | $item = Item::findOne(3); 188 | $this->assertTrue($item->moveTo(2, false)->save()); 189 | 190 | $item = ItemWindow::findOne(10); 191 | $this->assertTrue($item->moveTo(4, true)->save()); 192 | 193 | $dataSet = $this->getConnection()->createDataSet(['item']); 194 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-to-update-same-scope.php')); 195 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 196 | } 197 | 198 | public function testMoveToUpdateOtherScope() 199 | { 200 | $item = Item::findOne(5); 201 | $item->parent_id = 6; 202 | $this->assertTrue($item->moveTo(-1, true)->save()); 203 | 204 | $item = ItemWindow::findOne(7); 205 | $item->parent_id = 1; 206 | $this->assertTrue($item->moveTo(0, false)->save()); 207 | 208 | $dataSet = $this->getConnection()->createDataSet(['item']); 209 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-to-update-other-scope.php')); 210 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 211 | } 212 | 213 | public function testMoveToUpdateSelf() 214 | { 215 | $item = Item::findOne(2); 216 | $this->assertTrue($item->moveTo(1, false)->save()); 217 | 218 | $item = ItemWindow::findOne(13); 219 | $this->assertTrue($item->moveTo(2, true)->save()); 220 | 221 | $dataSet = $this->getConnection()->createDataSet(['item']); 222 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/data.php')); 223 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 224 | } 225 | 226 | public function testMoveBefore() 227 | { 228 | $item = Item::findOne(7); 229 | $this->assertTrue($item->moveBefore(Item::findOne(10))->save()); 230 | 231 | $item = ItemWindow::findOne(5); 232 | $this->assertTrue($item->moveBefore(Item::findOne(2))->save()); 233 | 234 | $dataSet = $this->getConnection()->createDataSet(['item']); 235 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-before.php')); 236 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 237 | } 238 | 239 | public function testMoveAfter() 240 | { 241 | $item = Item::findOne(4); 242 | $this->assertTrue($item->moveAfter(Item::findOne(3))->save()); 243 | 244 | $item = ItemWindow::findOne(8); 245 | $this->assertTrue($item->moveAfter(Item::findOne(11))->save()); 246 | 247 | $dataSet = $this->getConnection()->createDataSet(['item']); 248 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-move-after.php')); 249 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 250 | } 251 | 252 | public function testReorder() 253 | { 254 | $item = Item::findOne(7); 255 | $this->assertEquals(8, $item->reorder(false)); 256 | 257 | $item = ItemWindow::findOne(4); 258 | $this->assertEquals(4, $item->reorder(true)); 259 | 260 | $dataSet = $this->getConnection()->createDataSet(['item']); 261 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-reorder.php')); 262 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 263 | } 264 | 265 | public function testInsert() 266 | { 267 | $item = new Item(['parent_id' => 1, 'slug' => 'new1']); 268 | $this->assertTrue($item->save()); 269 | 270 | $item = new ItemWindow(['parent_id' => 6, 'slug' => 'new2']); 271 | $this->assertTrue($item->save()); 272 | 273 | $dataSet = $this->getConnection()->createDataSet(['item']); 274 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/test-insert.php')); 275 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 276 | } 277 | 278 | public function testUpdate() 279 | { 280 | $item = Item::findOne(4); 281 | $this->assertTrue($item->save()); 282 | 283 | $item = ItemWindow::findOne(8); 284 | $this->assertTrue($item->save()); 285 | 286 | $dataSet = $this->getConnection()->createDataSet(['item']); 287 | $expectedDataSet = new ArrayDataSet(require(__DIR__ . '/data/data.php')); 288 | $this->assertDataSetsEqual($expectedDataSet, $dataSet); 289 | } 290 | } -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | defined('YII_DEBUG') or define('YII_DEBUG', true); 9 | defined('YII_ENV') or define('YII_ENV', 'test'); 10 | 11 | require(__DIR__ . '/../vendor/autoload.php'); 12 | require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); 13 | 14 | Yii::setAlias('@tests', __DIR__); 15 | 16 | new \yii\console\Application([ 17 | 'id' => 'unit', 18 | 'basePath' => __DIR__, 19 | ]); -------------------------------------------------------------------------------- /tests/data/config.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'dsn' => 'sqlite::memory:', 5 | ], 6 | 'mysql' => [ 7 | 'dsn' => 'mysql:host=localhost;dbname=test', 8 | 'username' => 'root', 9 | 'password' => '', 10 | 'charset' => 'utf8', 11 | ], 12 | 'mssql' => [ 13 | 'dsn' => 'sqlsrv:Server=localhost;Database=test', 14 | 'username' => '', 15 | 'password' => '', 16 | ], 17 | 'pgsql' => [ 18 | 'dsn' => 'pgsql:host=localhost;dbname=test;port=5432;', 19 | 'username' => 'postgres', 20 | 'password' => 'postgres', 21 | ], 22 | ]; 23 | 24 | if (is_file(__DIR__ . '/config.local.php')) { 25 | include(__DIR__ . '/config.local.php'); 26 | } 27 | 28 | return $config; -------------------------------------------------------------------------------- /tests/data/data.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '0','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | ) 20 | ); -------------------------------------------------------------------------------- /tests/data/empty.php: -------------------------------------------------------------------------------- 1 | array() 4 | ); -------------------------------------------------------------------------------- /tests/data/test-insert.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '0','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | 20 | array('id' => '15','parent_id' => '1','sort' => '102','slug' => 'new1'), 21 | array('id' => '16','parent_id' => '6','sort' => '106','slug' => 'new2'), 22 | ) 23 | ); -------------------------------------------------------------------------------- /tests/data/test-move-after.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '2','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '0','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '3','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '4','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | ) 20 | ); -------------------------------------------------------------------------------- /tests/data/test-move-before.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '-1','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-2','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '0','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '0','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | ) 20 | ); -------------------------------------------------------------------------------- /tests/data/test-move-first-insert-in-empty.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '0','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | 20 | array('id' => '15','parent_id' => '4','sort' => '0','slug' => 'new1'), 21 | array('id' => '16','parent_id' => '11','sort' => '0','slug' => 'new2'), 22 | ) 23 | ); -------------------------------------------------------------------------------- /tests/data/test-move-first-insert-in-no-empty.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '0','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | 20 | array('id' => '15','parent_id' => '1','sort' => '-101','slug' => 'new1'), 21 | array('id' => '16','parent_id' => '6','sort' => '-103','slug' => 'new2'), 22 | ) 23 | ); -------------------------------------------------------------------------------- /tests/data/test-move-first-update-other-scope.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '6','sort' => '-103','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '1','sort' => '-101','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | ) 20 | ); -------------------------------------------------------------------------------- /tests/data/test-move-first-update-same-scope.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '-101','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '0','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '-103','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | ) 20 | ); -------------------------------------------------------------------------------- /tests/data/test-move-last-insert-in-empty.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '0','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | 20 | array('id' => '15','parent_id' => '4','sort' => '0','slug' => 'new1'), 21 | array('id' => '16','parent_id' => '11','sort' => '0','slug' => 'new2'), 22 | ) 23 | ); -------------------------------------------------------------------------------- /tests/data/test-move-last-insert-in-no-empty.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '0','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | 20 | array('id' => '15','parent_id' => '1','sort' => '102','slug' => 'new1'), 21 | array('id' => '16','parent_id' => '6','sort' => '106','slug' => 'new2'), 22 | ) 23 | ); -------------------------------------------------------------------------------- /tests/data/test-move-last-update-other-scope.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '6','sort' => '106','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '1','sort' => '102','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | ) 20 | ); -------------------------------------------------------------------------------- /tests/data/test-move-last-update-same-scope.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '102','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '106','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | ) 20 | ); -------------------------------------------------------------------------------- /tests/data/test-move-to-insert-gap.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '0','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '2','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | 20 | array('id' => '15','parent_id' => '1','sort' => '-2','slug' => 'new1'), 21 | array('id' => '16','parent_id' => '6','sort' => '4','slug' => 'new2'), 22 | ) 23 | ); -------------------------------------------------------------------------------- /tests/data/test-move-to-insert-no-gap.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '2','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '1','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '3','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '0','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '2','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '1','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | 20 | array('id' => '15','parent_id' => '1','sort' => '0','slug' => 'new1'), 21 | array('id' => '16','parent_id' => '6','sort' => '3','slug' => 'new2'), 22 | ) 23 | ); -------------------------------------------------------------------------------- /tests/data/test-move-to-update-other-scope.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '1','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '-1','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-2','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '6','sort' => '-1','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '1','sort' => '0','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '0','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '1','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | ) 20 | ); -------------------------------------------------------------------------------- /tests/data/test-move-to-update-same-scope.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '0','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '2','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-1','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '1','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '-3','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '6','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '-1','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '4','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '3','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '-2','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '2','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '5','slug' => 'n8'), 19 | ) 20 | ); -------------------------------------------------------------------------------- /tests/data/test-reorder.php: -------------------------------------------------------------------------------- 1 | array( 4 | array('id' => '1','parent_id' => NULL,'sort' => '0','slug' => 'r'), 5 | array('id' => '2','parent_id' => '1','sort' => '0','slug' => 'n1'), 6 | array('id' => '3','parent_id' => '1','sort' => '-100','slug' => 'n2'), 7 | array('id' => '4','parent_id' => '1','sort' => '-200','slug' => 'n3'), 8 | array('id' => '5','parent_id' => '1','sort' => '100','slug' => 'n4'), 9 | 10 | array('id' => '6','parent_id' => NULL,'sort' => '0','slug' => 'r'), 11 | array('id' => '7','parent_id' => '6','sort' => '0','slug' => 'n1'), 12 | array('id' => '8','parent_id' => '6','sort' => '700','slug' => 'n2'), 13 | array('id' => '9','parent_id' => '6','sort' => '200','slug' => 'n3'), 14 | array('id' => '10','parent_id' => '6','sort' => '300','slug' => 'n4'), 15 | array('id' => '11','parent_id' => '6','sort' => '500','slug' => 'n5'), 16 | array('id' => '12','parent_id' => '6','sort' => '100','slug' => 'n6'), 17 | array('id' => '13','parent_id' => '6','sort' => '400','slug' => 'n7'), 18 | array('id' => '14','parent_id' => '6','sort' => '600','slug' => 'n8'), 19 | ) 20 | ); -------------------------------------------------------------------------------- /tests/migrations/TestMigration.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable\tests\migrations; 9 | 10 | use yii\db\Schema; 11 | use yii\db\Migration; 12 | 13 | /** 14 | * @author PaulZi 15 | */ 16 | class TestMigration extends Migration 17 | { 18 | public function up() 19 | { 20 | ob_start(); 21 | $tableOptions = null; 22 | if ($this->db->driverName === 'mysql') { 23 | // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci 24 | $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; 25 | } 26 | 27 | // tree 28 | if ($this->db->getTableSchema('{{%item}}', true) !== null) { 29 | $this->dropTable('{{%item}}'); 30 | } 31 | $this->createTable('{{%item}}', [ 32 | 'id' => Schema::TYPE_PK, 33 | 'parent_id' => Schema::TYPE_INTEGER . ' NULL', 34 | 'sort' => Schema::TYPE_INTEGER . ' NOT NULL', 35 | 'slug' => Schema::TYPE_STRING . ' NOT NULL', 36 | ], $tableOptions); 37 | $this->createIndex('parent_sort', '{{%item}}', ['parent_id', 'sort']); 38 | 39 | // update cache (sqlite bug) 40 | $this->db->getSchema()->getTableSchema('{{%item}}', true); 41 | ob_end_clean(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/models/Item.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable\tests\models; 9 | 10 | use paulzi\sortable\SortableBehavior; 11 | 12 | /** 13 | * @author PaulZi 14 | * 15 | * @property integer $id 16 | * @property integer $parent_id 17 | * @property integer $sort 18 | * @property string $slug 19 | * 20 | * 21 | * @method static Item|null findOne() findOne($condition) 22 | * 23 | * @mixin SortableBehavior 24 | */ 25 | class Item extends \yii\db\ActiveRecord 26 | { 27 | /** 28 | * @inheritdoc 29 | */ 30 | public static function tableName() 31 | { 32 | return '{{%item}}'; 33 | } 34 | /** 35 | * @inheritdoc 36 | */ 37 | public function behaviors() 38 | { 39 | return [ 40 | [ 41 | 'class' => SortableBehavior::className(), 42 | 'query' => ['parent_id'], 43 | ], 44 | ]; 45 | } 46 | } -------------------------------------------------------------------------------- /tests/models/ItemWindow.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable\tests\models; 9 | 10 | use paulzi\sortable\SortableBehavior; 11 | 12 | /** 13 | * @author PaulZi 14 | * 15 | * @property integer $id 16 | * @property integer $parent_id 17 | * @property integer $sort 18 | * @property string $slug 19 | * 20 | * 21 | * @method static ItemWindow|null findOne() findOne($condition) 22 | * 23 | * @mixin SortableBehavior 24 | */ 25 | class ItemWindow extends \yii\db\ActiveRecord 26 | { 27 | /** 28 | * @inheritdoc 29 | */ 30 | public static function tableName() 31 | { 32 | return '{{%item}}'; 33 | } 34 | /** 35 | * @inheritdoc 36 | */ 37 | public function behaviors() 38 | { 39 | return [ 40 | [ 41 | 'class' => SortableBehavior::className(), 42 | 'query' => function ($model) { 43 | /** @var ItemWindow $model */ 44 | $tableName = $model->tableName(); 45 | return $model->find()->andWhere(["{$tableName}.[[parent_id]]" => $model->parent_id]); 46 | }, 47 | 'joinMode' => false, 48 | ], 49 | ]; 50 | } 51 | } -------------------------------------------------------------------------------- /tests/mysql/SortableBehaviorTest.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable\tests\mysql; 9 | 10 | use paulzi\sortable\tests\SortableBehaviorTestCase; 11 | 12 | /** 13 | * @group mysql 14 | * 15 | * @author PaulZi 16 | */ 17 | class SortableBehaviorTest extends SortableBehaviorTestCase 18 | { 19 | protected static $driverName = 'mysql'; 20 | } -------------------------------------------------------------------------------- /tests/pgsql/SortableBehaviorTest.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable\tests\pgsql; 9 | 10 | use paulzi\sortable\tests\SortableBehaviorTestCase; 11 | 12 | /** 13 | * @group pgsql 14 | * 15 | * @author PaulZi 16 | */ 17 | class SortableBehaviorTest extends SortableBehaviorTestCase 18 | { 19 | protected static $driverName = 'pgsql'; 20 | } -------------------------------------------------------------------------------- /tests/sqlite/SortableBehaviorTest.php: -------------------------------------------------------------------------------- 1 | 5 | * @license MIT (https://github.com/paulzi/yii2-sortable/blob/master/LICENSE) 6 | */ 7 | 8 | namespace paulzi\sortable\tests\sqlite; 9 | 10 | use paulzi\sortable\tests\SortableBehaviorTestCase; 11 | 12 | /** 13 | * @group sqlite 14 | * 15 | * @author PaulZi 16 | */ 17 | class SortableBehaviorTest extends SortableBehaviorTestCase 18 | { 19 | protected static $driverName = 'sqlite'; 20 | } --------------------------------------------------------------------------------