PHPackages                             infoweb-internet-solutions/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. [Framework](/categories/framework)
4. /
5. infoweb-internet-solutions/yii2-sortable-behavior

ActiveYii2-extension[Framework](/categories/framework)

infoweb-internet-solutions/yii2-sortable-behavior
=================================================

Sort ActiveRecords and related records in Yii2.

1.0.1(11y ago)04221MITPHP

Since Jan 5Pushed 10y ago3 watchersCompare

[ Source](https://github.com/infoweb-internet-solutions/yii2-sortable-behavior)[ Packagist](https://packagist.org/packages/infoweb-internet-solutions/yii2-sortable-behavior)[ RSS](/packages/infoweb-internet-solutions-yii2-sortable-behavior/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (3)Versions (3)Used By (1)

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

[](#yii2-sortable)

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.

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:

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

Or run:

`$ php composer.phar require infoweb/yii2-sortable-behavior "*"`

You can manually install **Sortable** by [downloading the source in ZIP-format](https://github.com/infoweb-internet-solutions/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 the jQuery UI Sortable functionality). If an item is dropped on a new position, an Ajax-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 two extra configurable properties:

#### orderUrl

[](#orderurl)

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

#### sortOptions

[](#sortoptions)

`array`. The options for the jQuery sortable object. See .

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

Default: `[]` (empty array).

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 )`

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.

---

### 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, say on preference.

---

### 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 an one-to-many relation, like we would normally do (notice the `orderBy` clause):

```
class Director extends ActiveRecord	{
	...
	public function getMovies( ) {
    	return $this->hasMany( Movie::className( ), ['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::className( );
    }

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

```

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 house keeping 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::className( );
    }

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

```

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::className( )
            ]
        ];
    }
	...
}

```

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
] );
...

...

```

###  Health Score

30

—

LowBetter than 64% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity12

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

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 ~118 days

Total

2

Last Release

4026d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7a977fc12b7f6eba9255863c5d9512e400457c69dd67a4b1df82dbc38908768d?d=identicon)[infoweb-internet-solutions](/maintainers/infoweb-internet-solutions)

---

Top Contributors

[![sjaakp](https://avatars.githubusercontent.com/u/5585878?v=4)](https://github.com/sjaakp "sjaakp (5 commits)")[![rubenheymans](https://avatars.githubusercontent.com/u/2543870?v=4)](https://github.com/rubenheymans "rubenheymans (4 commits)")[![infoweb-internet-solutions](https://avatars.githubusercontent.com/u/8706156?v=4)](https://github.com/infoweb-internet-solutions "infoweb-internet-solutions (3 commits)")

---

Tags

sortyii2extensionBehaviorwidgetyiiorder

### Embed Badge

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

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

###  Alternatives

[sjaakp/yii2-sortable-behavior

Sort ActiveRecords and related records in Yii2.

36144.7k](/packages/sjaakp-yii2-sortable-behavior)[kartik-v/yii2-dynagrid

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

743.1M27](/packages/kartik-v-yii2-dynagrid)[miloschuman/yii2-highcharts-widget

Highcharts widget for Yii 2 Framework.

1761.5M14](/packages/miloschuman-yii2-highcharts-widget)[skeeks/cms

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

13825.6k47](/packages/skeeks-cms)[sjaakp/yii2-taggable

Manage tags of ActiveRecord in Yii2.

3030.6k](/packages/sjaakp-yii2-taggable)[thiagotalma/yii2-fullcalendar

Widget for Yii Framework 2.0 to use FullCalendar

1777.7k](/packages/thiagotalma-yii2-fullcalendar)

PHPackages © 2026

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