Eloquent Sortable:為 Eloquent 模型添加可排序功能

套件名稱:spatie/eloquent-sortable

介紹

spatie/eloquent-sortable 是由 Spatie 開發的 Laravel 套件,用於為 Eloquent 模型加入「排序功能」,常見應用包含:

  • 排序前台選單
  • 調整資料顯示順序
  • 後台拖曳排序功能

主要功能:

  • 自動或手動處理資料排序欄位(例如 order_column
  • 提供多種排序操作,例如移動至特定位置、重新排序
  • 語法簡單,支援自動排序與分群排序邏輯

安裝

composer require spatie/eloquent-sortable

使用步驟

1. 資料表中新增排序欄位

php artisan make:migration add_order_column_to_your_table
// migration 範例
Schema::table('your_table', function (Blueprint $table) {
    $table->integer('order_column')->nullable()->after('id');
});

2. 在模型中使用 Sortable Trait

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

class MenuItem extends Model implements Sortable
{
    use SortableTrait;

    public $sortable = [
        'order_column_name' => 'order_column', // 自訂排序欄位
        'sort_when_creating' => true,          // 建立時自動排在最後
    ];
}

3. 取得排序後資料

MenuItem::ordered()->get();

4. 手動更新排序

$menuItem->moveOrderUp();     // 向上移動一格
$menuItem->moveOrderDown();   // 向下移動一格
$menuItem->moveToStart();     // 移動到最前面
$menuItem->moveToEnd();       // 移動到最後面
$menuItem->moveToPosition(3); // 移動到第 3 個位置(從 1 開始)

額外設定選項(進階)

你可以覆寫以下方法來客製排序邏輯,例如依據群組、分類進行排序:

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

實務應用場景

應用場景 描述
前台選單排序 使用者可調整選單順序
後台列表排序 管理者可拖曳排序
商品顯示排序 控制商品在前台顯示順序
課程章節排序 調整課程章節播放順序

補充說明:處理階層結構的排序

spatie/eloquent-sortable 本身不支援多層階層結構的排序,但可搭配 parent_id 欄位及客製化排序邏輯實現。

階層資料結構範例

選單
- 首頁
- 關於我們
  - 公司介紹
  - 團隊成員
- 服務項目
  - Web 開發
  - App 設計

資料表結構範例

Schema::create('menu_items', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->unsignedBigInteger('parent_id')->nullable();
    $table->integer('order_column')->nullable();
});

模型中定義階層關聯與排序條件

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

class MenuItem extends Model implements Sortable
{
    use SortableTrait;

    protected $fillable = ['title', 'parent_id', 'order_column'];

    public function parent()
    {
        return $this->belongsTo(MenuItem::class, 'parent_id');
    }

    public function children()
    {
        return $this->hasMany(MenuItem::class, 'parent_id');
    }

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

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

排序用法(支援單層群組)

// 根目錄下的排序
MenuItem::whereNull('parent_id')->ordered()->get();

// 特定父選單下的子項排序
$parent->children()->ordered()->get();

延伸閱讀