Yii 2 Sortable
This extension allows to manage order of ActiveRecord models via different behaviors.
Choose one to fit your needs., (*1)
, (*2)
Installation
The preferred way to install this extension is through composer., (*3)
Either run, (*4)
php composer.phar require --prefer-dist arogachev/yii2-sortable
or add, (*5)
"arogachev/yii2-sortable": "*"
to the require section of your composer.json
file., (*6)
Features
- Several implemented algorithms. Choose one to fit your needs.
- Setting of sortable scope. In this case the order of models in each scope is managed separately.
- Additional setting of sortable condition. For example, if the model can be marked as active or deleted,
you can additionally specify that condition and it will be considered when changing these attributes values.
- Order auto adjustment when adding new model, removing out of sortable scope,
moving between the sortable scopes.
- Changing order of models inside sortable scope.
- GUI for managing order of models in
GridView
(SortableColumn
).
- Sort controller for simplifying writing of own GUI
Behaviors types
There are several behaviors to choose from:, (*7)
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., (*8)
ContinuousNumericalSortableBehavior
:, (*9)
Stored number is equal to exact position., (*10)
Advantages:, (*11)
- You can get current position without additional queries
Disadvantages:, (*12)
- Amount of
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
:, (*13)
The numbers are stored with certain intervals (initially with equal size).
You can see the basic description of the used algorithm here., (*14)
Advantages:, (*15)
- For adding or deletion there is no need to adjust order of other models. And for changing order for most of the times
only few
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:, (*16)
- For getting positions of models extra query is used.
Preparing table structure
In case of using numerical behaviors add this to migration:, (*17)
$this->addColumn('table_name', 'sort', Schema::TYPE_INTEGER . ' NOT NULL');
Attaching behavior
Add this to your model for minimal setup:, (*18)
use arogachev\sortable\behaviors\numerical\ContinuousNumericalSortableBehavior;
/**
* @inheritdoc
*/
public function behaviors()
{
return [
[
'class' => ContinuousNumericalSortableBehavior::className(),
],
];
}
Configuring behavior
Common properties for all behaviors:, (*19)
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:, (*20)
function () {
return Question::find()->where(['test_id' => $this->test_id]);
}
You can use $model
parameter to generate model related queries:, (*21)
function ($model) {
return $model->getNeighbors();
}
where getNeighbors()
implementation can be like this:, (*22)
/**
* @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., (*23)
sortableCondition
- additional property to filter sortable models. You should specify it as conditional array:, (*24)
[
'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., (*25)
access
- closure for checking access to sort for current user. Example:, (*26)
function () {
return Yii::$app->user->can('questions.sort');
}
Numerical behaviors properties:, (*27)
sortAttribute
- name of the sort attribute column. Defaults to sort
., (*28)
IntervalNumericalSortableBehavior
properties:, (*29)
intervalSize
- size of the interval. Defaults to 1000
. When specifying bigger numbers,
conflicts will happen less often., (*30)
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
., (*31)
Changing order of models inside sortable scope
The behavior provides few methods to change any sortable model order:, (*32)
-
moveToPosition($position)
- basic method for moving model to any position inside sortable scope
-
moveBefore($pk = null)
- move model before another model of this sortable scope.
If $pk
is not specified it will be moved to the very end
-
moveAfter($pk = null)
- move model after another model of this sortable scope
If $pk
is not specified it will be moved to the very beginning
-
moveBack()
- move back by one position
-
moveForward()
- move forward by one position
-
moveAsFirst()
- move to the very beginning
-
moveAsLast()
- move to the very end
GUI for changing order
There is special SortableColumn
for GridView
., (*33)
Features:, (*34)
- It doesn't force you to use the whole another
GridView
- No need to attach additional actions every time
- Multiple
GridView
on one page support
- Displaying current position
- Inline editing of current position
- Moving with drag and drop (with
jQuery UI Sortable
) with special handle icon,
so you can interact with other data without triggering sort change
- Moving back and forward by one position
- Moving as first and last
Include once this to your application config:, (*35)
'controllerMap' => [
'sort' => [
'class' => 'arogachev\sortable\controllers\SortController',
],
],
Then configure GridView
:, (*36)
- Wrap it with
Pjax
widget for working without page reload
- Add
id
for unchangeable root container
- Include column in
columns
section
use arogachev\sortable\grid\SortableColumn;
= 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
],
]) ?>
You can configure display through template
and buttons
properties (similar to ActionColumn)., (*37)
The available tags are:, (*38)
currentPosition
moveWithDragAndDrop
moveForward
moveBack
moveAsFirst
moveAsLast
You can extend it with your own. Example of overriding:, (*39)
'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'),
]);
},
],
Custom GUI for changing order
If you want to write your own GUI for changing order without using GridView
, you can use the SortController
actions:, (*40)
-
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
:, (*41)
-
modelClass
- model full class name with namespace
-
modelPk
- moved model primary key value (pass object in case of primary keys)