PHPackages                             rutorika/sortable - 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. rutorika/sortable

ActiveLibrary[Database &amp; ORM](/categories/database)

rutorika/sortable
=================

Adds sortable behavior and ordering to Laravel Eloquent models. Grouping and many to many supported.

9.3.0(1y ago)299992.5k↓11.2%4914MITPHPPHP &gt;=7.2.5

Since Jun 28Pushed 1y ago6 watchersCompare

[ Source](https://github.com/boxfrommars/rutorika-sortable)[ Packagist](https://packagist.org/packages/rutorika/sortable)[ RSS](/packages/rutorika-sortable/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (44)Used By (14)

[![Build Status](https://camo.githubusercontent.com/9f203b11e8fcdb93e9c7cc8c5246af992b75075952238f4a18e69b8ce081dd4f/68747470733a2f2f7472617669732d63692e6f72672f626f7866726f6d6d6172732f7275746f72696b612d736f727461626c652e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/boxfrommars/rutorika-sortable) [![Scrutinizer Code Quality](https://camo.githubusercontent.com/d8b015be50a3c0fccebdaa3ed92b1ce6d1e5a98379e0984a43abed50ceefc3e0/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f626f7866726f6d6d6172732f7275746f72696b612d736f727461626c652f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/boxfrommars/rutorika-sortable/?branch=master) [![Latest Stable Version](https://camo.githubusercontent.com/325d216e2c8f2809f54cd748780da97887c7aa080be055819f61dbb4d9666fdf/68747470733a2f2f706f7365722e707567782e6f72672f7275746f72696b612f736f727461626c652f762f737461626c65)](https://packagist.org/packages/rutorika/sortable) [![Total Downloads](https://camo.githubusercontent.com/517d912627261a0c893f2ca6413841d05289373d38b8b3111a575d2303832f79/68747470733a2f2f706f7365722e707567782e6f72672f7275746f72696b612f736f727461626c652f646f776e6c6f616473)](https://packagist.org/packages/rutorika/sortable) [![Latest Unstable Version](https://camo.githubusercontent.com/5faad47dff075da5d731d354a1da7e4b64a210be541df0ac8e3ff21b85b9303a/68747470733a2f2f706f7365722e707567782e6f72672f7275746f72696b612f736f727461626c652f762f756e737461626c65)](https://packagist.org/packages/rutorika/sortable) [![License](https://camo.githubusercontent.com/1610ffbfac3aafa9b01914af22629bbd46b69d1fb7874bfef73f741bcaea58c8/68747470733a2f2f706f7365722e707567782e6f72672f7275746f72696b612f736f727461626c652f6c6963656e7365)](https://packagist.org/packages/rutorika/sortable)

Laravel 5 - Demo
----------------

[](#laravel-5---demo)

Install
-------

[](#install)

Install package through Composer

```
composer require rutorika/sortable
```

### Version Compatibility

[](#version-compatibility)

LaravelRutorika Sortable41.2.x (branch laravel4)&lt;=5.33.2.x5.43.4.x5.54.2.x5.74.7.x6.06.0.x7.x, 8.x8.x.x9.x, 10.x, 11.x, 12.x9.x.xSortable Trait
--------------

[](#sortable-trait)

Adds sortable behavior to Eloquent (Laravel) models

### Usage

[](#usage)

Add `position` field to your model (see below how to change this name):

```
// schema builder example
public function up()
{
    Schema::create('articles', function (Blueprint $table) {
        // ... other fields ...
        $table->integer('position'); // Your model must have position field:
    });
}
```

Add `\Rutorika\Sortable\SortableTrait` to your Eloquent model.

```
class Article extends Model
{
    use \Rutorika\Sortable\SortableTrait;
}
```

if you want to use custom column name for position, set `$sortableField`:

```
class Article extends Model
{
    use \Rutorika\Sortable\SortableTrait;

    protected static $sortableField = 'somefield';
}
```

Now you can move your entities with methods `moveBefore($entity)` and `moveAfter($entity)` (you dont need to save model after that, it has saved already):

```
$entity = Article::find(1);

$positionEntity = Article::find(10)

$entity->moveAfter($positionEntity);

// if $positionEntity->position is 14, then $entity->position is 15 now
```

Also this trait automatically defines entity position on the `create` event, so you do not need to add `position` manually, just create entities as usual:

```
$article = new Article();
$article->title = $faker->sentence(2);
$article->description = $faker->paragraph();
$article->save();
```

This entity will be at position `entitiesMaximumPosition + 1`

To get ordered entities use the `sorted` scope:

```
$articles = Article::sorted()->get();
```

> \*\* Note \*\*: Resorting does not take place after a record is deleted. Gaps in positional values do not affect the ordering of your lists. However, if you prefer to prevent gaps you can reposition your models using the `deleting` event. Something like:

```
// YourAppServiceProvider

YourModel::deleting(function ($model) {
    $model->next()->decrement('position');
});
```

> You need rutorika-sortable &gt;=2.3 to use `->next()`

### Sortable groups

[](#sortable-groups)

if you want group entity ordering by field, add to your model

```
protected static $sortableGroupField = 'fieldName';
```

now moving and ordering will be encapsulated by this field.

If you want group entity ordering by many fields, use as an array:

```
protected static $sortableGroupField = ['fieldName1','fieldName2'];
```

### Sortable many to many

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

Let's assume your database structure is

```
posts
    id
    title

tags
    id
    title

post_tag
    post_id
    tag_id

```

and you want to order *tags* for each *post*

Add `position` column to the pivot table (you can use any name you want, but `position` is used by default)

```
post_tag
    post_id
    tag_id
    position

```

Add `\Rutorika\Sortable\BelongsToSortedManyTrait` to your `Post` model and define `belongsToSortedMany` relation provided by this trait:

```
class Post extends Model {

    use BelongsToSortedManyTrait;

    public function tags()
    {
        return $this->belongsToSortedMany('\App\Tag');
    }
}
```

> Note: `$this->belongsToSortedMany` has different signature then `$this->belongsToMany` -- the second argument for this method is `$orderColumn` (`'position'` by default), next arguments are the same

Attaching tags to post with `save`/`sync`/`attach` methods will set proper position

```
    $post->tags()->save($tag) // or
    $post->tags()->attach($tag->id) // or
    $post->tags()->sync([$tagId1, $tagId2, /* ...tagIds */])
```

Getting related model is sorted by position

```
$post->tags; // ordered by position by default
```

You can reorder tags for given post

```
    $post->tags()->moveBefore($entityToMove, $whereToMoveEntity); // or
    $post->tags()->moveAfter($entityToMove, $whereToMoveEntity);
```

Many to many demo:  ([code](https://github.com/boxfrommars/rutorika-sortable-demo5))

You can also use polymorphic many to many relation with sortable behavour by using the `MorphsToSortedManyTrait` trait and returning `$this->morphToSortedMany()` from relation method.

By following the Laravel polymorphic many to many table relation your tables should look like

```
posts
    id
    title

tags
    id
    title

taggables
    tag_id
    position
    taggable_id
    taggable_type

```

And your model like

```
class Post extends Model {

    use MorphToSortedManyTrait;

    public function tags()
    {
        return $this->morphToSortedMany('\App\Tag', 'taggable');
    }
}
```

Sortable Controller
-------------------

[](#sortable-controller)

Also this package provides `\Rutorika\Sortable\SortableController`, which handle requests to sort entities

### Usage

[](#usage-1)

Add the service provider to `config/app.php`

```
'providers' => array(
    // providers...

    'Rutorika\Sortable\SortableServiceProvider',
)
```

publish the config:

```
php artisan vendor:publish
```

Add models you need to sort in the config `config/sortable.php`:

```
'entities' => array(
     'articles' => '\App\Article', // entityNameForUseInRequest => ModelName
     // or
     'articles' => ['entity' => '\App\Article'],
     // or for many to many
     'posts' => [
        'entity' => '\App\Post',
        'relation' => 'tags' // relation name (method name which returns $this->belongsToSortedMany)
     ]
),
```

Add route to the `sort` method of the controller:

```
Route::post('sort', '\Rutorika\Sortable\SortableController@sort');
```

Now if you post to this route valid data:

```
$validator = \Validator::make(\Input::all(), array(
    'type' => array('required', 'in:moveAfter,moveBefore'), // type of move, moveAfter or moveBefore
    'entityName' => array('required', 'in:' . implode(',', array_keys($sortableEntities))), // entity name, 'articles' in this example
    'positionEntityId' => 'required|numeric', // id of relative entity
    'id' => 'required|numeric', // entity id
));

// or for many to many

$validator = \Validator::make(\Input::all(), array(
    'type' => array('required', 'in:moveAfter,moveBefore'), // type of move, moveAfter or moveBefore
    'entityName' => array('required', 'in:' . implode(',', array_keys($sortableEntities))), // entity name, 'articles' in this example
    'positionEntityId' => 'required|numeric', // id of relative entity
    'id' => 'required|numeric', // entity id
    'parentId' => 'required|numeric', // parent entity id
));
```

Then entity with `\Input::get('id')` id will be moved relative by entity with `\Input::get('positionEntityId')` id.

For example, if request data is:

```
type:moveAfter
entityName:articles
id:3
positionEntityId:14

```

then the article with id 3 will be moved after the article with id 14.

### jQuery UI sortable example

[](#jquery-ui-sortable-example)

> Note: Laravel 5 has csrf middleware enabled by default, so you should setup ajax requests:

Template

```

    @foreach ($articles as $article)

        {{{ $article->id }}}
        {{{ $article->title }}}

    @endforeach

```

Template for many to many ordering

```

    @foreach ($post->tags as $tag)

        {{ $tag->id }}
        {{ $tag->title }}

    @endforeach

```

```
    /**
     *
     * @param type string 'insertAfter' or 'insertBefore'
     * @param entityName
     * @param id
     * @param positionId
     */
    var changePosition = function(requestData){
        $.ajax({
            'url': '/sort',
            'type': 'POST',
            'data': requestData,
            'success': function(data) {
                if (data.success) {
                    console.log('Saved!');
                } else {
                    console.error(data.errors);
                }
            },
            'error': function(){
                console.error('Something wrong!');
            }
        });
    };

    $(document).ready(function(){
        var $sortableTable = $('.sortable');
        if ($sortableTable.length > 0) {
            $sortableTable.sortable({
                handle: '.sortable-handle',
                axis: 'y',
                update: function(a, b){

                    var entityName = $(this).data('entityname');
                    var $sorted = b.item;

                    var $previous = $sorted.prev();
                    var $next = $sorted.next();

                    if ($previous.length > 0) {
                        changePosition({
                            parentId: $sorted.data('parentid'),
                            type: 'moveAfter',
                            entityName: entityName,
                            id: $sorted.data('itemid'),
                            positionEntityId: $previous.data('itemid')
                        });
                    } else if ($next.length > 0) {
                        changePosition({
                            parentId: $sorted.data('parentid'),
                            type: 'moveBefore',
                            entityName: entityName,
                            id: $sorted.data('itemid'),
                            positionEntityId: $next.data('itemid')
                        });
                    } else {
                        console.error('Something wrong!');
                    }
                },
                cursor: "move"
            });
        }
    });
```

Development
-----------

[](#development)

```
sudo docker build -t rutorika-sortable .
sudo docker run --volume $PWD:/project --rm --interactive --tty --user $(id -u):$(id -g) rutorika-sortable vendor/bin/phpunit

```

###  Health Score

56

—

FairBetter than 98% of packages

Maintenance46

Moderate activity, may be stable

Popularity59

Moderate usage in the ecosystem

Community36

Small or concentrated contributor base

Maturity71

Established project with proven stability

 Bus Factor1

Top contributor holds 81.4% 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 ~101 days

Recently: every ~417 days

Total

40

Last Release

403d ago

Major Versions

3.5.0 → 4.02017-08-31

4.7.1 → 6.0.02019-09-16

6.0.0 → 7.0.02020-03-14

7.0.1 → 8.0.02020-09-13

8.0.0 → 9.0.02022-02-21

PHP version history (5 changes)0.9PHP &gt;=5.4.0

3.4.0PHP &gt;=5.6.4

4.0PHP &gt;=7

4.7PHP &gt;=7.1.3

7.0.0PHP &gt;=7.2.5

### Community

Maintainers

![](https://www.gravatar.com/avatar/5fc842172477343df481485c5331a329e844387ea3d5dbc1e00d966bdf270492?d=identicon)[boxfrommars](/maintainers/boxfrommars)

---

Top Contributors

[![boxfrommars](https://avatars.githubusercontent.com/u/81930?v=4)](https://github.com/boxfrommars "boxfrommars (175 commits)")[![vluzrmos](https://avatars.githubusercontent.com/u/450848?v=4)](https://github.com/vluzrmos "vluzrmos (7 commits)")[![giordanolima](https://avatars.githubusercontent.com/u/8314190?v=4)](https://github.com/giordanolima "giordanolima (5 commits)")[![mvrkljan](https://avatars.githubusercontent.com/u/694929?v=4)](https://github.com/mvrkljan "mvrkljan (4 commits)")[![Chrissi2812](https://avatars.githubusercontent.com/u/6141652?v=4)](https://github.com/Chrissi2812 "Chrissi2812 (4 commits)")[![timothyasp](https://avatars.githubusercontent.com/u/707699?v=4)](https://github.com/timothyasp "timothyasp (4 commits)")[![dath](https://avatars.githubusercontent.com/u/2124173?v=4)](https://github.com/dath "dath (3 commits)")[![peterbabic](https://avatars.githubusercontent.com/u/3952052?v=4)](https://github.com/peterbabic "peterbabic (2 commits)")[![lkrempler](https://avatars.githubusercontent.com/u/5972830?v=4)](https://github.com/lkrempler "lkrempler (2 commits)")[![jmagdev](https://avatars.githubusercontent.com/u/590288?v=4)](https://github.com/jmagdev "jmagdev (1 commits)")[![scrutinizer-auto-fixer](https://avatars.githubusercontent.com/u/6253494?v=4)](https://github.com/scrutinizer-auto-fixer "scrutinizer-auto-fixer (1 commits)")[![SRWieZ](https://avatars.githubusercontent.com/u/1408020?v=4)](https://github.com/SRWieZ "SRWieZ (1 commits)")[![stevebauman](https://avatars.githubusercontent.com/u/6421846?v=4)](https://github.com/stevebauman "stevebauman (1 commits)")[![fabianmu](https://avatars.githubusercontent.com/u/89517?v=4)](https://github.com/fabianmu "fabianmu (1 commits)")[![tonystatic](https://avatars.githubusercontent.com/u/5255487?v=4)](https://github.com/tonystatic "tonystatic (1 commits)")[![christoph-kluge](https://avatars.githubusercontent.com/u/1446269?v=4)](https://github.com/christoph-kluge "christoph-kluge (1 commits)")[![YOzaz](https://avatars.githubusercontent.com/u/5859318?v=4)](https://github.com/YOzaz "YOzaz (1 commits)")[![ZAZmaster](https://avatars.githubusercontent.com/u/7642406?v=4)](https://github.com/ZAZmaster "ZAZmaster (1 commits)")

---

Tags

laravelorderingphpsortablesortinglaraveleloquentsortablesortlaravel5laravel4sortingorderorderingorderable

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/rutorika-sortable/health.svg)

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

###  Alternatives

[spatie/eloquent-sortable

Sortable behaviour for eloquent models

1.5k22.9M268](/packages/spatie-eloquent-sortable)[jedrzej/pimpable

Laravel 4/5/6 package that allows to dynamically filter, sort and eager load relations for your models using request parameters

105179.0k1](/packages/jedrzej-pimpable)[jedrzej/sortable

Sortable trait for Laravel's Eloquent models - sort your models using request parameters

54261.0k1](/packages/jedrzej-sortable)[highsolutions/eloquent-sequence

A Laravel package for easy creation and management sequence support for Eloquent models with elastic configuration.

121130.3k](/packages/highsolutions-eloquent-sequence)[indexzer0/eloquent-filtering

Powerful eloquent filtering

22425.9k3](/packages/indexzer0-eloquent-filtering)[sjdaws/vocal

Recursive functionality for Eloquent in Laravel 4 and 5

4315.2k1](/packages/sjdaws-vocal)

PHPackages © 2026

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