Sortable behaviour for Eloquent models

Overview

Sortable behaviour for Eloquent models

Latest Version GitHub Workflow Status Software License Total Downloads

This package provides a trait that adds sortable behaviour to an Eloquent model.

The value of the order column of a new record of a model is determined by the maximum value of the order column of all records of that model + 1.

The package also provides a query scope to fetch all the records in the right order.

Spatie is a webdesign agency in Antwerp, Belgium. You'll find an overview of all our open source projects on our website.

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

Installation

For Laravel 6.x or PHP 7.x, use version 3.x of this package.

This package can be installed through Composer.

composer require spatie/eloquent-sortable

In Laravel 5.5 and above the service provider will automatically get registered. In older versions of the framework just add the service provider in config/app.php file:

'providers' => [
    ...
    Spatie\EloquentSortable\EloquentSortableServiceProvider::class,
];

Optionally you can publish the config file with:

php artisan vendor:publish --provider="Spatie\EloquentSortable\EloquentSortableServiceProvider" --tag="config"

This is the content of the file that will be published in config/eloquent-sortable.php

return [
  /*
   * The name of the column that will be used to sort models.
   */
  'order_column_name' => 'order_column',

  /*
   * Define if the models should sort when creating. When true, the package
   * will automatically assign the highest order number to a new model
   */
  'sort_when_creating' => true,
];

Usage

To add sortable behaviour to your model you must:

  1. Implement the Spatie\EloquentSortable\Sortable interface.
  2. Use the trait Spatie\EloquentSortable\SortableTrait.
  3. Optionally specify which column will be used as the order column. The default is order_column.

Example

use Spatie\EloquentSortable\Sortable;
use Spatie\EloquentSortable\SortableTrait;

class MyModel extends Eloquent implements Sortable
{

    use SortableTrait;

    public $sortable = [
        'order_column_name' => 'order_column',
        'sort_when_creating' => true,
    ];

    ...
}

If you don't set a value $sortable['order_column_name'] the package will assume that your order column name will be named order_column.

If you don't set a value $sortable['sort_when_creating'] the package will automatically assign the highest order number to a new model;

Assuming that the db-table for MyModel is empty:

$myModel = new MyModel();
$myModel->save(); // order_column for this record will be set to 1

$myModel = new MyModel();
$myModel->save(); // order_column for this record will be set to 2

$myModel = new MyModel();
$myModel->save(); // order_column for this record will be set to 3


//the trait also provides the ordered query scope
$orderedRecords = MyModel::ordered()->get();

You can set a new order for all the records using the setNewOrder-method

/**
 * the record for model id 3 will have order_column value 1
 * the record for model id 1 will have order_column value 2
 * the record for model id 2 will have order_column value 3
 */
MyModel::setNewOrder([3,1,2]);

Optionally you can pass the starting order number as the second argument.

/**
 * the record for model id 3 will have order_column value 11
 * the record for model id 1 will have order_column value 12
 * the record for model id 2 will have order_column value 13
 */
MyModel::setNewOrder([3,1,2], 10);

To sort using a column other than the primary key, use the setNewOrderByCustomColumn-method.

/**
 * the record for model uuid '7a051131-d387-4276-bfda-e7c376099715' will have order_column value 1
 * the record for model uuid '40324562-c7ca-4c69-8018-aff81bff8c95' will have order_column value 2
 * the record for model uuid '5dc4d0f4-0c88-43a4-b293-7c7902a3cfd1' will have order_column value 3
 */
MyModel::setNewOrderByCustomColumn('uuid', [
   '7a051131-d387-4276-bfda-e7c376099715',
   '40324562-c7ca-4c69-8018-aff81bff8c95',
   '5dc4d0f4-0c88-43a4-b293-7c7902a3cfd1'
]);

As with setNewOrder, setNewOrderByCustomColumn will also accept an optional starting order argument.

/**
 * the record for model uuid '7a051131-d387-4276-bfda-e7c376099715' will have order_column value 10
 * the record for model uuid '40324562-c7ca-4c69-8018-aff81bff8c95' will have order_column value 11
 * the record for model uuid '5dc4d0f4-0c88-43a4-b293-7c7902a3cfd1' will have order_column value 12
 */
MyModel::setNewOrderByCustomColumn('uuid', [
   '7a051131-d387-4276-bfda-e7c376099715',
   '40324562-c7ca-4c69-8018-aff81bff8c95',
   '5dc4d0f4-0c88-43a4-b293-7c7902a3cfd1'
], 10);

You can also move a model up or down with these methods:

$myModel->moveOrderDown();
$myModel->moveOrderUp();

You can also move a model to the first or last position:

$myModel->moveToStart();
$myModel->moveToEnd();

You can determine whether an element is first or last in order:

$myModel->isFirstInOrder();
$myModel->isLastInOrder();

You can swap the order of two models:

MyModel::swapOrder($myModel, $anotherModel);

Grouping

If your model/table has a grouping field (usually a foreign key): id, user_id, title, order_column and you'd like the above methods to take it into considerations, you can create a buildSortQuery method at your model:

  public function buildSortQuery()
    {
        return static::query()->where('user_id', $this->user_id);
    }

This will restrict the calculations to fields value of the model instance.

Tests

The package contains some integration/smoke tests, set up with Orchestra. The tests can be run via phpunit.

vendor/bin/phpunit

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

Alternatives

License

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

Comments
  • Add Feature: sorting scopes

    Add Feature: sorting scopes

    As I'm still working with Laravel 5.2, I created a fork of the 2.3.0 version. I found this feature useful, because I wanted to allow a user to sort his comments of a post - so I created a sort_scope to prevent sorting comments of other users.

    opened by zoomyboy 11
  • Rearranging Isn't Working

    Rearranging Isn't Working

    I'm using eloquent-sortable in my Laravel Admin CRUD table and everything is appearing okay, but when I try to reorder items, I get a success message but the items don't reorder.

    I wasn't able to find any guidelines on how the database tables should be set up, so I'm wondering if my order_column isn't set up correctly.

    Video Controller

    use SortableTrait;
    
    public $sortable = [
        'order_column_name' => 'order',
        'sort_when_creating' => true,
    ];
    ...
    $grid->order('Order')->orderable()->sortable();
    

    Database The database has a column order with integers, but they aren't unique. Hoping they don't NEED to be because it would be a pain to fix this.

    opened by cvassios-ekko 9
  • Allow setNewOrder() to accept a custom sort column

    Allow setNewOrder() to accept a custom sort column

    e.g., MyModel::setNewOrder(['09b25ab5-b921-4794-941c-2152982c24a6', 'ca11be8f-6faf-4848-bb9b-fc951193a66d', '09b25ab5-b921-4794-951c-2152982c24a6', 'eee4041c-4aa2-468b-bga0-82d70468409d', '2643003e-2335-4496-ba4d-df6b24caa551'], 10, 'uid');

    opened by RalphMRivera 6
  • Add at position

    Add at position

    What do you think about setting a desired order value when for a new model?

    $model->setOrder(3);
    

    This should place the given model at position 3 and move current models 3-X one up.

    opened by lukasoppermann 6
  • adding ability to move to specific position from current model location

    adding ability to move to specific position from current model location

    It would be nice to add a method on the trait that allows people to move a specific location.

    My proposed method.

    $myModel->moveToPosition($postion);
    

    I have created a working implementation and attached it to the PR. Please let me know if there are any questions or concerns.

    opened by fuzzybaird 5
  • Reset order when destroy

    Reset order when destroy

    Hi, after all thanks for the package, it´s super usefull!

    One question about destroy models. Is there no method to reset the positions? For example, I have the following models:

    [
      0 => [
        "title" => "Item 1",
        "order_column" => "1",
      ],
      1 => [
        "title" => "Item 1",
        "order_column" => "2",
      ],
      2 => [
        "title" => "Item 1",
        "order_column" => "3",
      ],
      3 => [
        "title" => "Item 1",
        "order_column" => "4",
      ],
    ]
    

    Now, if I destroy Item 3, there are a gap between orders 2 and 4. A method that reset all orders without gap??

    Actually, I do this, but I´m not sure if it´s the best option:

    public function destroy( Link $link )
    {
        $link->delete();
    
        $orderedLinks = Link::ordered()->pluck('id')->toArray();
    
        Link::setNewOrder($orderedLinks);
    
        return redirect()->back();
    }
    

    Thank you!!

    opened by Albvadi 5
  • Custom pivot model: order_column doesn't have a default value

    Custom pivot model: order_column doesn't have a default value

    Hello! I can't make work this behavior with custom pivot model. I have models:

    class Questionnaire extends Model
    {
        use HasTranslations;
    
        protected $fillable = [
            'title', 'is_default', 'type', 'company_id'
        ];
        public $translatable = ['title'];
    
        public function attributes()
        {
            return $this->belongsToMany(Attribute::class, 'questionnaire_attribute')->using(QuestionnaireAttribute::class);
        }
    }
    
    class Attribute extends Model
    {
        use HasTranslations;
    
        protected $fillable = [
            'name', 'title', 'type', 'icon', 'is_notify_filter'
        ];
        public $translatable = ['title'];
    
        public function questionnaires()
        {
            return $this->belongsToMany(Questionnaire::class, 'questionnaire_attribute')->using(QuestionnaireAttribute::class);
        }
    }
    
    class QuestionnaireAttribute extends Pivot implements Sortable
    {
        use HasTranslations, SortableTrait;
    
        protected $table = 'questionnaire_attribute';
        public $translatable = ['title'];
    
        public function questionnaire()
        {
            return $this->belongsTo(Questionnaire::class);
        }
    
        public function attribute()
        {
            return $this->belongsTo(Attribute::class);
        }
    }
    

    And when try to attach, I get this error:

    SQLSTATE[HY000]: General error: 1364 Field 'order_column' doesn't have a default value (SQL: insert into `questionnaire_attribute` (`attribute_id`, `is_clearable`, `is_required`, `questionnaire_id`, `title`) values (1, 0, 1, 1, {"en":"Test"}))
    

    Laravel 5.7, MariaDB 10.3.9

    opened by esl51 5
  • Added ability to sort models grouped by a column

    Added ability to sort models grouped by a column

    This pull request allows to sort models grouped by a given column. You can do this by adding to $sortable array the next params: 'group_column_name' => 'group_column', 'sort_by_group' => true,

    I don't know if it is a very concrete use case, but I think it can help for example when ordering intermediate database tables.

    opened by davidcb 5
  • Fails with unique index.

    Fails with unique index.

    Hey guys,

    I'm unsure if this is intentional, but I added this to a project that uses unique indexes on the order column, and it caused this package to fail because of that. I removed the unique index and it works perfectly.

    opened by matthewlilley 5
  • Improve speed

    Improve speed

    Please check this solution as a possible approach to reduce the amount of memory and queries the package uses in the method setNewOrder.

    First, we try to reduce the amount of information we load from DB, as the model could be fat and the number of records is unknown. Second, the number of queries is almost reduced in a half.

    I'm not sure if it'd be the correct way of doing this, regarding lines 56-59 (I'd like this properties to be static but...).

    We could have another way to solve this problem by replacing 61-73 of this PR with just:

    foreach ($ids as $id) {
        static::where($primaryKeyColumn, $id)->update([$orderColumnName => $startOrder++]);
    }
    

    Thanks!

    opened by driade 5
  • Exception thrown on using ordered() scope

    Exception thrown on using ordered() scope

    Argument 1 passed to App\Model::scopeOrdered() must be an instance of Illuminate\Database\Query\Builder, instance of Illuminate\Database\Eloquent\Builder given

    I have defined scopes myself in the model and seems I didn't use any typehinting at all for $query. So this could be a possible fix?

    bug 
    opened by Propaganistas 5
Releases(4.0.1)
Owner
Spatie
Webdesign agency based in Antwerp, Belgium
Spatie
Add tags and taggable behaviour to your Laravel app

Add tags and taggable behaviour to a Laravel app This package offers taggable behaviour for your models. After the package is installed the only thing

Spatie 1.4k Dec 29, 2022
Custom Blade components to add sortable/drag-and-drop HTML elements in your apps.

Laravel Blade Sortable Demo Repo Installation You can install the package via composer: composer require asantibanez/laravel-blade-sortable After the

Andrés Santibáñez 370 Dec 23, 2022
Laravel-admin grid-sortable

laravel-admin grid-sortable This extension can help you sort by dragging the rows of the data list, the front end is based on jQueryUI sortable, and t

Extensions for laravel-admin 37 Oct 14, 2022
An Eloquent Way To Filter Laravel Models And Their Relationships

Eloquent Filter An Eloquent way to filter Eloquent Models and their relationships Introduction Lets say we want to return a list of users filtered by

Eric Tucker 1.5k Jan 7, 2023
Easy creation of slugs for your Eloquent models in Laravel

Eloquent-Sluggable Easy creation of slugs for your Eloquent models in Laravel. NOTE: These instructions are for the latest version of Laravel. If you

Colin Viebrock 3.6k Dec 30, 2022
This package gives Eloquent models the ability to manage their friendships.

Laravel 5 Friendships This package gives Eloquent models the ability to manage their friendships. You can easily design a Facebook like Friend System.

Alex Kyriakidis 690 Nov 27, 2022
Automatically validating Eloquent models for Laravel

Validating, a validation trait for Laravel Validating is a trait for Laravel Eloquent models which ensures that models meet their validation criteria

Dwight Watson 955 Dec 25, 2022
Laravel Ban simplify blocking and banning Eloquent models.

Laravel Ban Introduction Laravel Ban simplify management of Eloquent model's ban. Make any model bannable in a minutes! Use case is not limited to Use

cybercog 879 Dec 30, 2022
cybercog 996 Dec 28, 2022
Create presenters for Eloquent Models

Laravel Presentable This package allows the information to be presented in a different way by means of methods that can be defined in the model's pres

The Hive Team 67 Dec 7, 2022
A small package for adding UUIDs to Eloquent models.

A small package for adding UUIDs to Eloquent models. Installation You can install the package via composer: composer require ryangjchandler/laravel-uu

Ryan Chandler 40 Jun 5, 2022
Use auto generated UUID slugs to identify and retrieve your Eloquent models.

Laravel Eloquent UUID slug Summary About Features Requirements Installation Examples Compatibility table Alternatives Tests About By default, when get

Khalyomede 25 Dec 14, 2022
Record created by, updated by and deleted by on Eloquent models automatically.

quarks/laravel-auditors Record created by, updated by and deleted by (if SoftDeletes added) on Eloquent models automatically. Installation composer re

Quarks 3 Jun 13, 2022
Observe (and react to) attribute changes made on Eloquent models.

Laravel Attribute Observer Requirements PHP: 7.4+ Laravel: 7+ Installation You can install the package via composer: composer require alexstewartja/la

Alex Stewart 55 Jan 4, 2023
Tag support for Laravel Eloquent models - Taggable Trait

Laravel Taggable Trait This package is not meant to handle javascript or html in any way. This package handles database storage and read/writes only.

Rob 859 Dec 11, 2022
Preferences for Laravel Eloquent models

Preferences for Laravel Eloquent models Use this library to bind multiple key/value pair preferences to your application's Eloquent models. Preference

Kevin Laude 32 Oct 30, 2022
Laravel package to search through multiple Eloquent models.

Laravel package to search through multiple Eloquent models. Supports sorting, pagination, scoped queries, eager load relationships and searching through single or multiple columns.

Protone Media 845 Jan 1, 2023
Eloquent Befriended brings social media-like features like following, blocking and filtering content based on following or blocked models.

Laravel Befriended Eloquent Befriended brings social media-like features like following, blocking and filtering content based on following or blocked

Renoki Co. 720 Jan 3, 2023
Cascading deletes for Eloquent models that implement soft deletes

Cascading soft deletes for the Laravel PHP Framework Introduction In scenarios when you delete a parent record - say for example a blog post - you may

Michael Dyrynda 767 Jan 6, 2023