PHPackages                             rethinking/eloquent-sortable-relations - 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. rethinking/eloquent-sortable-relations

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

rethinking/eloquent-sortable-relations
======================================

Sortable behaviour for eloquent hasMany relations

1.1.0(6y ago)02MITPHPPHP ^7.2CI failing

Since May 9Pushed 6y ago1 watchersCompare

[ Source](https://github.com/re-thinking/eloquent-sortable-relations)[ Packagist](https://packagist.org/packages/rethinking/eloquent-sortable-relations)[ RSS](/packages/rethinking-eloquent-sortable-relations/feed)WikiDiscussions master Synced today

READMEChangelogDependencies (3)Versions (3)Used By (0)

Eloquent sortable relations
===========================

[](#eloquent-sortable-relations)

[![run-tests](https://github.com/re-thinking/eloquent-sortable-relations/workflows/run-tests/badge.svg?branch=master)](https://github.com/re-thinking/eloquent-sortable-relations/workflows/run-tests/badge.svg?branch=master)[![Software License](https://camo.githubusercontent.com/55c0218c8f8009f06ad4ddae837ddd05301481fcf0dff8e0ed9dadda8780713e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265)](LICENSE)[![StyleCI](https://camo.githubusercontent.com/d1ddf2313881996bb0d00918fbbe371a43b035e10a8c6f5f55e47c5efeb3e2dc/68747470733a2f2f7374796c6563692e696f2f7265706f732f3236313737313230372f736869656c643f6272616e63683d6d6173746572)](https://styleci.io/repos/261771207)

This package provides a new relation to Eloquent model

The value of the related model position column is determined by the maximum value of this column for parent record (through foreign key).

The package provides a relation class itself, traits for owner side and related side and an interface.

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

[](#installation)

This package can be installed through composer

```
composer require rethinking/eloquent-sortable-relations
```

Usage
-----

[](#usage)

To add sortable behaviour for your relations follow next steps:

1. Use `Rethinking\Eloquent\Relations\Sortable\HasSortedRelations` trait on the **owning** Model
2. Implement `Rethinking\Eloquent\Relations\Sortable\Sortable` interface on **related** Model. For simplicity, use `Rethinking\Eloquent\Relations\Sortable\HasSortingContext` trait
3. Define the *sorting context* for Model (the owning side)
4. Optionally define the column name for position attribute on your model (`position` by default in trait) by overriding trait method `HasSortingContext::getPositionColumnName(): string`

### Example

[](#example)

Owning side:

```
use Illuminate\Database\Eloquent\Model;
use Rethinking\Eloquent\Relations\Sortable\HasSortedRelations;

class Owner extends Model
{
    use HasSortedRelations;

    public function items()
    {
        return $this->hasManySorted(RelatedItem::class);
    }
}
```

Related side:

```
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Rethinking\Eloquent\Relations\Sortable\HasSortingContext;
use Rethinking\Eloquent\Relations\Sortable\Sortable;

class RelatedItem extends Model implements Sortable
{
    use HasSortingContext;

    public function owner(): BelongsTo
    {
        return $this->belongsTo(Owner::class);
    }

    public function getSortingContext(): BelongsTo
    {
        return $this->owner();
    }
}
```

By default the sorting column name assumed to be *position*For modifying this, override (in case trait is used) or implement the interface method

```
public static function getPositionColumnName(): string
{
    return 'position';
}
```

For manually setting sorting order for existing items, use method `HasMnaySorted::setSortingOrder(iterable $ids, $start = 1)`This will set the array index of $id + $start as position field

```
//assume $owner has 3 related items with id 1,2,3 and positions of 1,2,3
$owner = Owner::find(1);

$owner->items()->setSortingOrder([2,3,1]);

$ids = $owner->items->pluck('id')->toArray(); //ids = [2,3,1]
```

In case, position sequence corrupted, you can manually trigger the `HasManySorted::resort()` method.

```
//assume $owner has 3 related items with corresponding positions of [1,3,5]
$owner = Owner::find(1);

$owner->items()->resort();

$positions = $owner->items->pluck('position')->toArray(); //$positions = [1,2,3]
```

When creating the new relation for owning side, the position will be automatically calculated as maximum position value for owner foreign key `max(position) + 1`.

Assume, owner has no related items. The calculation of the next position will be triggered when using any of the following methods:

```
$owner = Owner::find(1);

$owner->items()->create();         //newly created item will have position = 1

$owner->items()->save(new Item()); //newly created item will have position = 2

$item = new Item();
$item->owner()->associate($owner);
$item->save();                     //newly created item will have position = 3
```

When dissociate the related model from it owner, the position will be reset to `null`

```
$item = Item::find(2);

//assume $item has position = 2
$item->owner()->dissociate();

assert($item->position === null); //true
```

### Relations

[](#relations)

Extension provides 3 types of sorted relations:

#### `HasManySorted`

[](#hasmanysorted)

Items will be fetched based on position within `Owner::class`:

```
//assume $owner has 3 related items with id 1,2,3 and positions of 3,1,2
$owner = Owner::find(1);

$items = $owner->items()->get(); // items will be sorted by position i.e. ids will be [$item2,$item1,$item3]
```

#### `HasManySortedThrough`

[](#hasmanysortedthrough)

Items will be fetched through `Middle::class` based on position of `Item::class` within `Middle::class`:

```
//assume you have $owner -> $middle -> $item, where $item is sorted by $middle context

use Illuminate\Database\Eloquent\Model;
use Rethinking\Eloquent\Relations\Sortable\HasSortedRelations;
use Rethinking\Eloquent\Relations\Sortable\HasSortingContext;use Rethinking\Eloquent\Relations\Sortable\Sortable;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Owner extends Model
{
    use HasSortedRelations;

    public function middles()
    {
        return $this->hasMany(Middle::class);
    }

    public function items()
    {
        return $this->hasManySortedThrough(Item::class, Middle::class);
    }
}

class Middle extends Model
{
    use HasSortedRelations;

    public function items()
    {
        return $this->hasManySorted(Item::class);
    }

    public function owner()
    {
        return $this->belongsTo(Owner::class);
    }
}

class Item extends Model implements Sortable
{
    use HasSortingContext;

    public function middle()
    {
        return $this->belongsTo(Middle::class);
    }

    public function getSortingContext(): BelongsTo
    {
        return $this->middle();
    }
}

//assume $owner has two $middle, each $middle has 3 $item
//items inside every $middle has sorting of [1,2,3]
$owner = Owner::find(1);

//items will be mixed i.e. $middle1Item1, $middle2Item2, $middle1Item2, $middle2Item2 etc.
$items = $owner->items()->get();
```

#### `HasManyThroughSorted`

[](#hasmanythroughsorted)

Items will be fetched through `Middle::class` based on position of `Middle::class` within `Owner::class`:

```
//assume you have $owner -> $middle -> $item, where $middle is sorted by $owner context

use Illuminate\Database\Eloquent\Model;
use Rethinking\Eloquent\Relations\Sortable\HasSortedRelations;
use Rethinking\Eloquent\Relations\Sortable\HasSortingContext;use Rethinking\Eloquent\Relations\Sortable\Sortable;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Owner extends Model
{
    use HasSortedRelations;

    public function middles()
    {
        return $this->hasMany(Middle::class);
    }

    public function items()
    {
        return $this->hasManySortedThrough(Item::class, Middle::class);
    }
}

class Middle extends Model implements Sortable
{
    use HasSortingContext;

    public function items()
    {
        return $this->hasMany(Item::class);
    }

    public function owner()
    {
        return $this->belongsTo(Owner::class);
    }

    public function getSortingContext() : BelongsTo
    {
        return $this->owner();
    }
}

class Item extends Model
{
    public function middle()
    {
        return $this->belongsTo(Middle::class);
    }
}

//assume $owner has two $middle, each $middle has 3 $item
//$middle1 has position 2
//$middle2 has position 1
$owner = Owner::find(1);

//items will be fetched from in order of $middle2 (position 1) and $middle1 (position 2)
$items = $owner->items()->get();
```

Test
----

[](#test)

The package contains integration tests, set up with Orchestra. Tests can be run through composer script

```
composer test
```

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

###  Health Score

22

—

LowBetter than 21% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity2

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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 ~2 days

Total

2

Last Release

2242d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/21132466?v=4)[Bogdan](/maintainers/re-thinking)[@re-thinking](https://github.com/re-thinking)

---

Top Contributors

[![re-thinking](https://avatars.githubusercontent.com/u/21132466?v=4)](https://github.com/re-thinking "re-thinking (11 commits)")

---

Tags

modeleloquentsortablesortbehaviourrelationsortedhasmany

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/rethinking-eloquent-sortable-relations/health.svg)

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

###  Alternatives

[spatie/eloquent-sortable

Sortable behaviour for eloquent models

1.5k25.7M319](/packages/spatie-eloquent-sortable)[anourvalar/eloquent-serialize

Laravel Query Builder (Eloquent) serialization

11223.5M33](/packages/anourvalar-eloquent-serialize)[jedrzej/pimpable

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

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

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

52272.9k1](/packages/jedrzej-sortable)[akaunting/laravel-sortable

Sortable behavior package for Laravel

27195.7k](/packages/akaunting-laravel-sortable)[nevadskiy/laravel-position

Arrange Laravel models in a given order.

2687.6k](/packages/nevadskiy-laravel-position)

PHPackages © 2026

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