【Laravel】Eloquentをソート順に並び替える

【Laravel】Eloquentをソート順に並び替える

Eloquentを使用しているLaravelのモデルに並び順を追加・操作する機能を付与したい場合には、spatie/eloquent-sortableが非常に便利だったので紹介します。

インストール

$ composer require spatie/eloquent-sortable

モデルの定義

インストールが完了したら、ソートしたいモデルにimplements Sortable, use SortableTrait及びpublic $sortableを追加します。

<?php

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

class MyModel extends Model implements Sortable
{

    use SortableTrait;

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

データの取得は以下のように行います。

$orderedRecords = MyModel::ordered()->get(); 

デフォルトでソートを適用する場合は、以下のようなコードを追加します。

protected static function boot()
{
    parent::boot();

    static::addGlobalScope('ordered', static function (Builder $builder) {
        $builder->ordered();
    });
}

ソートを解除してデータを取得することも可能です。

$orderedRecords = MyModel::withoutGlobalScope()->get();

並び替え

ソート順の変更は以下のように行います。

並び替え

MyModel::setNewOrder([3,1,2]);

上に移動

$myModel->moveOrderUp();

下に移動

$myModel->moveOrderDown();

一番最初に移動

$myModel->moveToStart();

一番最後に移動

$myModel->moveToEnd();

順番を入れ替える

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

グルーピング

外部キーごとにソート番号を管理したい場合は、モデルに以下のようなコードを追加することで実装できます。

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

タイムスタンプ

デフォルトでは、並び替え順を変更した際にupdated_atのようなタイムスタンプが更新されてしまいます。

更新を防ぐには以下のコードをモデルに追加してください。

<?php

class MyModel extends Model
{

    // flag
    protected static $ignoreTimestamps = false;

    // setter
    public static function ignoreTimestamps(bool $ignore = true)
    {
        static::$ignoreTimestamps = $ignore;
    }

    // override the method in the HasAttributes trait to also check for the custom flag
    public function usesTimestamps()
    {
        return parent::usesTimestamps() && !static::$ignoreTimestamps;
    }

   // ...

}

並び替え変更時

MyModel::ignoreTimestamps();
MyModel::setNewOrder($order);
MyModel::ignoreTimestamps(false);

参考文献