This extension allows to manage order of ActiveRecord models via different behaviors. Choose one to fit your needs.
The preferred way to install this extension is through composer.
Either run
php composer.phar require --prefer-dist arogachev/yii2-sortable
or add
"arogachev/yii2-sortable": "*"
to the require section of your composer.json
file.
GridView
(SortableColumn
).There are several behaviors to choose from:
ContinuousNumericalSortableBehavior
IntervalNumericalSortableBehavior
LinkedListSortableBehavior
(currently not implemented)The first two are numerical behaviors and they have one thing in common - they store position of each model as number.
ContinuousNumericalSortableBehavior
:
Stored number is equal to exact position.
Advantages:
Disadvantages:
UPDATE
queries can be large depending on amount of sortable models and situation.
It relates to adjustment order. For example no extra queries will be performed in case of switching models
with 3 and 4 position (only 2 UPDATE
queries). But if you have 1000 models and you move the last model
to the very beginning there will be 1000 UPDATE
queries (so it depends on interval length).IntervalNumericalSortableBehavior
:
The numbers are stored with certain intervals (initially with equal size). You can see the basic description of the used algorithm here.
Advantages:
SELECT
and one UPDATE
query will be executed.
The full adjustment of order of all models inside of sortable scope is only required in case of conflict.
The conflicts don't happen often if you set interval size big enough
and don't move models to the same position over and over again.Disadvantages:
In case of using numerical behaviors add this to migration:
$this->addColumn('table_name', 'sort', Schema::TYPE_INTEGER . ' NOT NULL');
Add this to your model for minimal setup:
use arogachev\sortable\behaviors\numerical\ContinuousNumericalSortableBehavior;
/**
* @inheritdoc
*/
public function behaviors()
{
return [
[
'class' => ContinuousNumericalSortableBehavior::className(),
],
];
}
Common properties for all behaviors:
scope
- sortable scope. Specify it if you want to separate models by condition
and manage order independently in each one. It expects closure returning ActiveQuery
, but where
part must be
specified as array only. Example:
function () {
return Question::find()->where(['test_id' => $this->test_id]);
}
You can use $model
parameter to generate model related queries:
function ($model) {
return $model->getNeighbors();
}
where getNeighbors()
implementation can be like this:
/**
* @return \yii\db\ActiveQuery
*/
public function getNeighbors()
{
return static::find()->where(['parent_id' => $this->parent_id]);
}
If this property is not set, all models considered as one sortable scope.
sortableCondition
- additional property to filter sortable models. You should specify it as conditional array:
[
'is_active' => 1,
'is_deleted' => 0,
],
prependAdded
- insert added sortable model to the beginning of sortable scope. Defaults to false
which means
inserting to the end.
access
- closure for checking access to sort for current user. Example:
function () {
return Yii::$app->user->can('questions.sort');
}
Numerical behaviors properties:
sortAttribute
- name of the sort attribute column. Defaults to sort
.
IntervalNumericalSortableBehavior
properties:
intervalSize
- size of the interval. Defaults to 1000
. When specifying bigger numbers,
conflicts will happen less often.
increasingLimit
- the number of times user can continuously move item to the end of the sortable scope.
Used to prevent increasing of numbers. Defaults to 10
.
The behavior provides few methods to change any sortable model order:
moveToPosition($position)
- basic method for moving model to any position inside sortable scopemoveBefore($pk = null)
- move model before another model of this sortable scope.
If $pk
is not specified it will be moved to the very endmoveAfter($pk = null)
- move model after another model of this sortable scope
If $pk
is not specified it will be moved to the very beginningmoveBack()
- move back by one positionmoveForward()
- move forward by one positionmoveAsFirst()
- move to the very beginningmoveAsLast()
- move to the very endThere is special SortableColumn
for GridView
.
Features:
GridView
GridView
on one page supportjQuery UI Sortable
) with special handle icon,
so you can interact with other data without triggering sort changeInclude once this to your application config:
'controllerMap' => [
'sort' => [
'class' => 'arogachev\sortable\controllers\SortController',
],
],
Then configure GridView
:
Pjax
widget for working without page reloadid
for unchangeable root containercolumns
sectionuse arogachev\sortable\grid\SortableColumn;
<div class="question-index" id="question-sortable">
<?php Pjax::begin(); ?>
<?= GridView::widget([
// Other configuration
'columns' => [
[
'class' => SortableColumn::className(),
'gridContainerId' => 'question-sortable',
'baseUrl' => '/sort/', // Optional, defaults to '/sort/'
'confirmMove' => true, // Optional, defaults to true
],
// Other columns
],
]) ?>
<?php Pjax::end(); ?>
</div>
You can configure display through template
and buttons
properties (similar to ActionColumn).
The available tags are:
currentPosition
moveWithDragAndDrop
moveForward
moveBack
moveAsFirst
moveAsLast
You can extend it with your own. Example of overriding:
'template' => '<div class="sortable-section">{moveWithDragAndDrop}</div>
<div class="sortable-section">{currentPosition}</div>
<div class="sortable-section">{moveForward} {moveBack}</div>',
'buttons' => [
'moveForward' => function () {
return Html::tag('i', '', [
'class' => 'fa fa-arrow-circle-left',
'title' => Yii::t('sortable', 'Move forward'),
]);
},
'moveBack' => function () {
return Html::tag('i', '', [
'class' => 'fa fa-arrow-circle-right',
'title' => Yii::t('sortable', 'Move back'),
]);
},
],
If you want to write your own GUI for changing order without using GridView
, you can use the SortController
actions:
move-before
(requires pk
of the next element after move sent via POST
)move-after
(requires pk
of the previous element after move sent via POST
)move-back
move-forward
move-as-first
move-as-last
move-to-position
(requires position
sent via POST
)For all of the actions these two parameters must exist in POST
:
modelClass
- model full class name with namespacemodelPk
- moved model primary key value (pass object in case of primary keys)