PHPackages                             sjaakp/yii2-sortable-behavior - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. sjaakp/yii2-sortable-behavior

ActiveYii2-extension[Database &amp; ORM](/categories/database)

sjaakp/yii2-sortable-behavior
=============================

Sort ActiveRecords and related records in Yii2.

1.2.1(2y ago)36169.5k↓41.1%16[1 issues](https://github.com/sjaakp/yii2-sortable-behavior/issues)[1 PRs](https://github.com/sjaakp/yii2-sortable-behavior/pulls)MITPHP

Since Jun 18Pushed 2y ago6 watchersCompare

[ Source](https://github.com/sjaakp/yii2-sortable-behavior)[ Packagist](https://packagist.org/packages/sjaakp/yii2-sortable-behavior)[ RSS](/packages/sjaakp-yii2-sortable-behavior/feed)WikiDiscussions master Synced 2d ago

READMEChangelog (5)Dependencies (2)Versions (7)Used By (0)

Yii2 Sortable
=============

[](#yii2-sortable)

[![Latest Stable Version](https://camo.githubusercontent.com/956289c780f172d34e25e832bbed5f7641565677847db140ddde085612f4da71/68747470733a2f2f706f7365722e707567782e6f72672f736a61616b702f796969322d736f727461626c652d6265686176696f722f762f737461626c65)](https://packagist.org/packages/sjaakp/yii2-sortable-behavior)[![Total Downloads](https://camo.githubusercontent.com/01749889bf92eb5658fa0784bd7451c668afbd0f7b647d02cf87691d5e798acd/68747470733a2f2f706f7365722e707567782e6f72672f736a61616b702f796969322d736f727461626c652d6265686176696f722f646f776e6c6f616473)](https://packagist.org/packages/sjaakp/yii2-sortable-behavior)[![License](https://camo.githubusercontent.com/08f1c624d6731e51e82b64403eac83cdd1982b6eb5df1decece438f226cb2f0d/68747470733a2f2f706f7365722e707567782e6f72672f736a61616b702f796969322d736f727461626c652d6265686176696f722f6c6963656e7365)](https://packagist.org/packages/sjaakp/yii2-sortable-behavior)

This package contains five classes to handle the sorting of ActiveRecords:

- **SortableGridView** - extended GridView widget;
- **SortableListView** - extended ListView widget;
- **Sortable** - ActiveRecord Behavior to handle the sorting of the records themselves, or of the one-to-many related records;
- **PivotRecord** - base class for the ActiveRecord of the pivot table in a many-to-many relation.
- **MMSortable** - ActiveRecord Behavior to handle the sorting of many-to-many related records.

The previous version of **Sortable** was dependent on [jQuery](https://jquery.com/). It now has no dependencies at all. It should work in all modern desktop browsers. At the time of writing the new HTML Drag and Drop, and thus **Sortable**, only works for few [mobile browsers](https://developer.mozilla.org/en-US/docs/Web/API/Document/dragenter_event#browser_compatibility).

The old [jQuery-dependent widgets](#sortable-with-jquery) are still available. Some may prefer their esthetics.

A demonstration of the **Sortable** suit is [here](http://www.sjaakpriester.nl/director/index).

Installation
------------

[](#installation)

The preferred way to install **Sortable** is through [Composer](https://getcomposer.org/). Either add the following to the `require` section of your `composer.json` file:

`"sjaakp/yii2-sortable-behavior": "*"`

Or run:

`composer require sjaakp/yii2-sortable-behavior "*"`

You can manually install **Sortable** by [downloading the source in ZIP-format](https://github.com/sjaakp/yii2-sortable-behavior/archive/master.zip).

SortableGridView and SortableListView
-------------------------------------

[](#sortablegridview-and-sortablelistview)

These widgets are derived from the standard GridView and ListView classes, but have one extra capability: the items can be moved to another position by means of drag and drop (using [HTML Drag and Drop](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API)functionality). If an item is dropped on a new position, a message is posted with the following data:

- `key`: the value of the item's primary key;
- `pos`: the zero-indexed new position of the item.

**SortableGridView** and **SortableListView** have one configurable property:

#### orderUrl

[](#orderurl)

`array|string`. The URL which is called after a sorting operation. The format is that of `yii\helpers\Url::toRoute`.

**SortableGridView** and **SortableListView** don't go together with the [Pjax-widget](https://www.yiiframework.com/doc/api/2.0/yii-widgets-pjax). However, **Sortable** is not very usable with paged data-widgets anyway.

Sortable
--------

[](#sortable)

With this Behavior, an ActiveRecord becomes 'sortable'. It has one configurable property and one extra method:

#### orderAttribute

[](#orderattribute)

`string|array`. The order attribute(s) of the ActiveRecord.

This can take the following values:

- `string` - the order attribute name;
- `array` of:
    - `string` - the order attribute name,
    - `foreignKeyName => orderAttrName` - limit ordering to ActiveRecords with the same foreign key value, i.e. of the same owner.

Default is `"ord"`.

#### order()

[](#order)

`public function order( $newPosition, $foreignKeyName = null, where = [] )`

This method puts the owner on the new position `$newPosition` by manipulating the order attribute. The order attribute is a zero-indexed, contiguous integer.

If `$foreignKeyName` is `null` (default) all the records are ordered.

If it is a string, the ordering is restricted to the records with the same value of `$foreignKeyName`. `$foreignKeyName` must be a key in `orderAttribute`. This comes in handy with one-to-many relations.

`$where` is an array of extra conditions in the format used by
[QueryInterface `where()`](https://www.yiiframework.com/doc/api/2.0/yii-db-queryinterface#where()-detail). May be used to add extra foreign key restraints. Default: `[ ]` (empty array).

---

### Usage scenario 1

[](#usage-scenario-1)

Simple sorting
--------------

[](#simple-sorting)

Suppose we have a very simple table of movie titles:

```
CREATE TABLE movie (
  id int(10) unsigned NOT NULL AUTO_INCREMENT,
  ord int(10) unsigned NOT NULL,
  title tinytext NOT NULL,
  PRIMARY KEY (id)
)

```

Where `ord` will be our order attribute.

We can make the `Movie` ActiveRecord sortable like this:

```
class Movie extends ActiveRecord
{
    public function behaviors( ) {
	    return [
	        [
	            'class' => 'sjaakp\sortable\Sortable',
	        ],
	    ];
	}
	...
}

```

In the controller we define an `index` action and an `order` action:

```
class MovieController extends Controller
{
	...
    public function actionIndex( )
	{
	    $dataProvider = new ActiveDataProvider( [
	        'query' => Movie::find( )->orderBy( 'ord' ),	// notice the orderBy clause
	        'sort' => false,
	        'pagination' => false
	    ] );

	    return $this->render( 'index', [
	        'dataProvider' => $dataProvider,
	    ] );
	}
	...
    public function actionOrder( )   {
        $post = Yii::$app->request->post( );
        if (isset( $post['key'], $post['pos'] ))   {
            $this->findModel( $post['key'] )->order( $post['pos'] );
        }
    }
	...
}

```

In the `index` view, we use a **SortableGridView**:

```
use sjaakp\sortable\SortableGridView;
...

```

And bingo! The list of movie titles is now sortable by drag and drop.

---

### Usage scenario 2

[](#usage-scenario-2)

One-to-many sorting
-------------------

[](#one-to-many-sorting)

Suppose we also have a list of directors. Each director has many movies, each movie belongs to one director (thinking of the Coen brothers, I know this is not necessarily true in reality).

We add two columns to our `movie` table:

```
CREATE TABLE movie (
  id int(10) unsigned NOT NULL AUTO_INCREMENT,
  ord int(10) unsigned NOT NULL,
  title tinytext NOT NULL,
  director_id int(10) unsigned NOT NULL,
  director_ord int(10) unsigned NOT NULL,
  PRIMARY KEY (id)
)

```

Where `director_ord` is the order attribute just for movies belonging to the same director.

In the `Director` model we define a one-to-many relation, like we would normally do (notice the `orderBy` clause):

```
class Director extends ActiveRecord	{
	...
	public function getMovies( ) {
    	return $this->hasMany( Movie::class, ['director_id' => 'id'] )
    	    ->orderBy( 'director_ord' );
	}
	...
}

```

The `Movie` model is sortable like before, but with another `orderAttribute`:

```
class Movie extends ActiveRecord
{
    public function behaviors( ) {
	    return [
	        [
	            'class' => 'sjaakp\sortable\Sortable',
                'orderAttribute' => [
                    'director_id' => 'director_ord'
	            ]
	        ],
	    ];
	}
	...
}

```

This time, `DirectorController` sports an extra action:

```
class DirectorController extends Controller
{
	...
    public function actionMovieOrder( )   {
        $post = Yii::$app->request->post( );
        if (isset( $post['key'], $post['pos'] ))   {
            $movie = Movie::findOne( $post['key'] );
            if ($movie) $movie->order( $post['pos'], 'director_id' );
        }
    }
	...
}

```

Let's now use a **SortableGridView** to display all the movies of the director in `director/view`:

```
use sjaakp\sortable\SortableGridView;
...
$movies = new ActiveDataProvider( [
	'query' => $model->getMovies( ),  // Do not use $model->movies, it returns array of Movies in stead of an ActiveQueryInterface
	'sort' => false,
	'pagination' => false
] );
...

...

```

Now each director's view shows a sortable list of his or her movies.

It's easy to combine Usage scenario's 1 and 2, so that *all* movies are sortable in `movie/index` and only the director's movies in `director/view`. Just initialize `Movie`'s **Sortable** behavior like this:

```
class Movie extends ActiveRecord
{
    public function behaviors( ) {
	    return [
	        [
	            'class' => 'sjaakp\sortable\Sortable',
                'orderAttribute' => [
					'ord',
                    'director_id' => 'director_ord'
	            ]
	        ],
	    ];
	}
	...
}

```

---

PivotRecord
-----------

[](#pivotrecord)

This is the base ActiveRecord for the pivot table of two sortable Models in a many-to-many relation.

The ordering information is stored in the pivot table as well.

A pivot table might look something like this:

```
CREATE TABLE actor_movie (
  actor_id int(10) unsigned NOT NULL,	# actor's primary key
  movie_id int(10) unsigned NOT NULL,	# movie's primary key
  actor_ord int(10) unsigned NOT NULL,	# actor's order
  movie_ord int(10) unsigned NOT NULL,	# movie's order
  PRIMARY KEY (actor_id,movie_id),
)

```

Using best practices it means:

- the **table name** is a concatenation of the two related table names in lexicographic order, separated by an underscore (`'_'`);
- the **primary key** column names consist of the related table name followed by `'_id'`;
- the **order** column names consist of the related table name followed by `'_ord'`;

Of course, it would be wise to add some indexes.

A concrete pivot record *has* to be derived from **PivotRecord**. Two static functions *must* be defined in the derived class:

#### aClass() and bClass()

[](#aclass-and-bclass)

`protected static function aClass( )``protected static function bClass( )`

These static member functions should return the fully qualified class names of the related Models.

`aClass` and `bClass` are completely equivalent. **PivotRecord** is in any respect a symmetric class.

A complete definition of a pivot record might look like this:

```
namespace app\models;
use sjaakp\sortable\PivotRecord;

class MovieActor extends PivotRecord    {

    protected static function aClass( )   {
        return Movie::class;
    }

    protected static function bClass( )   {
        return Actor::class;
    }
}

```

Notice that you can define some other static values as well, for special cases. Refer to the source code if you need this.

A **PivotRecord**-derived class has the following extra functions.

#### getAs() and getBs()

[](#getas-and-getbs)

`public static function getAs( ActiveRecord $b )`

Get the ordered records of `classA` belonging to `classB $b`.

`public static function getBs( ActiveRecord $a )`

Get the ordered records of `classB` belonging to `classA $a`.

The result is returned as an ActiveQuery, which can be modified further, or used as source of an ActiveDataProvider.

Notice these are **static** functions, not referring to any instantiation of the **PivotRecord**-derived class.

#### orderA() and orderB()

[](#ordera-and-orderb)

`public function orderA( $newPosition )`

Place `classA` at `$newPosition` in the list of all `classA`'s belonging to `classB`.

`public function orderB( $newPosition )`

Place `classB` at `$newPosition` in the list of all `classB`'s belonging to `classA`.

These are **member** functions. The id's of `classA` and `classB` are stored in the current **PivotRecord**.

MMSortable
----------

[](#mmsortable)

This is a Behavior of both partner ActiveRecords in a many-to-many relations. **PivotRecord** relies on it. **MMSortable** performs some housekeeping and has no (interesting) member functions. However, two properties *have* to be configured:

#### pivotClass

[](#pivotclass)

`string`. The fully classified class name of the pivot class (the **PivotRecord**-derived class).

#### pivotIdAttr

[](#pivotidattr)

`string`. The attribute name of the owner's id in the pivot class. If this is not set, it will be derived from the owner's class name; for instance: if the owner is class `Movie`, `$pivotIdAttr` will be `"movie_id"`.

---

### Usage scenario 3

[](#usage-scenario-3)

Many-to-many sorting
--------------------

[](#many-to-many-sorting)

Apart from our `movie` table, we also have an `actor` table. They are linked via an `actor_movie` pivot table: each movie can have many actors, and each actor can have many movies.

First, we define a pivot class, like so:

```
namespace app\models;
use sjaakp\sortable\PivotRecord;

class MovieActor extends PivotRecord    {

    protected static function aClass( )   {
        return Movie::class;
    }

    protected static function bClass( )   {
        return Actor::class;
    }
}

```

Then, we make sure that both `Movie` and `Actor` have a **MMSortable** Behavior:

```
class Movie extends ActiveRecord	{
	...
    public function behaviors( ) {
        return [
            [
                'class' => 'sjaakp\sortable\MMSortable',
                'pivotClass' => MovieActor::class
            ]
        ];
    }
	...
}

```

For convenience, we add a very simple member function to `Movie`:

```
class Movie extends ActiveRecord	{
	...
    public function getActors( ) {
        return MovieActor::getBs( $this );
    }
	...
}

```

Define an `order-actor`-action in `MovieController`:

```
class MovieController extends Controller
{
	...
    public function actionOrderActor( $id )   {
        $post = Yii::$app->request->post( );
        if (isset( $post['key'], $post['pos'] ))   {
            $piv = MovieActor::find( )->where( [
                'movie_id' => $id,
                'actor_id' => $post['key']
            ] )->one( );
            $piv->orderB( $post['pos'] );
        }
    }
	...
}

```

Now, in `movie/view`, we can display a **SortableGridView** with all the actors appearing in the movie.

```
use sjaakp\sortable\SortableGridView;
...
$actors = new ActiveDataProvider( [
	'query' => $model->getActors( ),
	'sort' => false,
	'pagination' => false
] );
...

...

```

---

Sortable with jQuery
--------------------

[](#sortable-with-jquery)

The previous version of **Sortable** (1.0) used [jQuery Draggable and Sortable](https://jqueryui.com/draggable/#sortable). The old jQuery-widgets are still available as **SortableGridViewJquery**and **SortableListViewJquery**. They are exchangeable with their non-jQuery counterparts.

You may prefer the esthetics of the jQuery variants. Also, the new HTML Drag and Drop may not work for all [mobile browsers](https://developer.mozilla.org/en-US/docs/Web/API/Document/dragenter_event#browser_compatibility).

**SortableGridViewJquery** and **SortableListViewJquery** have two extra configurable properties:

#### sortOptions

[](#sortoptions)

`array`. The options for the jQuery sortable object. See [https://api.jqueryui.com/sortable/](http://api.jqueryui.com/sortable/).

Notice that the options `'items'`, `'helper'`, and `'update'` will be overwritten.

Default: `[]` (empty array).

#### sortAxis

[](#sortaxis)

`boolean|string` The `'axis'` option for the jQuery sortable. If `false`, it is not set. Default: `'y'`.

For compatibility, **SortableGridView** and **SortableListView** have these options as well, but they are not functional.

---

Thanks
------

[](#thanks)

- **mike-kramer** (sortAxis option)
- **menshakov** (use updateAttributes)
- **robsch** (subtle order bug)
- **rubenheymans** (multiple foreign keys idea)

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance19

Infrequent updates — may be unmaintained

Popularity47

Moderate usage in the ecosystem

Community16

Small or concentrated contributor base

Maturity67

Established project with proven stability

 Bus Factor1

Top contributor holds 93.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~659 days

Recently: every ~500 days

Total

6

Last Release

734d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/156f636aceedc73021a2bfafb5433271c2c55d6cf4bce06b12902c85b4b491f1?d=identicon)[sjaakp](/maintainers/sjaakp)

---

Top Contributors

[![sjaakp](https://avatars.githubusercontent.com/u/5585878?v=4)](https://github.com/sjaakp "sjaakp (15 commits)")[![menshakov](https://avatars.githubusercontent.com/u/12133926?v=4)](https://github.com/menshakov "menshakov (1 commits)")

---

Tags

sortyii2extensionBehaviorwidgetyiiorder

### Embed Badge

![Health badge](/badges/sjaakp-yii2-sortable-behavior/health.svg)

```
[![Health](https://phpackages.com/badges/sjaakp-yii2-sortable-behavior/health.svg)](https://phpackages.com/packages/sjaakp-yii2-sortable-behavior)
```

###  Alternatives

[skeeks/cms

SkeekS CMS — control panel and tools based on php framework Yii2

13825.8k58](/packages/skeeks-cms)[kartik-v/yii2-dynagrid

Turbo charge the Yii 2 GridView with personalized columns, page size, and themes.

803.2M30](/packages/kartik-v-yii2-dynagrid)[sjaakp/yii2-taggable

Manage tags of ActiveRecord in Yii2.

2932.6k](/packages/sjaakp-yii2-taggable)[nanson/yii2-postgis

Yii2-extension to work with postgis data

1953.6k](/packages/nanson-yii2-postgis)[sjaakp/yii2-illustrated-behavior

ActiveRecord Behavior with associated Widget for Yii2.

423.1k](/packages/sjaakp-yii2-illustrated-behavior)[sjaakp/yii2-spatial

Yii2 ActiveRecord supporting MySQL spatial data

1878.8k1](/packages/sjaakp-yii2-spatial)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
