Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як зробити сортування перетягуванням через wire:sort і безпечно зберегти новий порядок?

Livewire 4 має вбудоване сортування перетягуванням - без сторонніх бібліотек.

<ul wire:sort="reorder">
    @foreach ($this->tasks as $task)
        <li wire:key="{{ $task->id }}" wire:sort:item="{{ $task->id }}">
            <span wire:sort:handle>⋮⋮</span>
            {{ $task->title }}
        </li>
    @endforeach
</ul>

Коли користувач відпускає елемент, Livewire викликає метод з ідентифікатором елемента (wire:sort:item) і новою позицією (з нуля). wire:sort:handle обмежує перетягування «ручкою».

Між кількома списками (канбан-дошка) - однакова група і ідентифікатор контейнера, що стає третім параметром:

<ul wire:sort="moveCard" wire:sort:group="cards" wire:sort:group-id="{{ $column->id }}">

Збереження порядку - ваша відповідальність, і тут легко помилитися:

public function reorder(int $taskId, int $position): void
{
    $this->authorize('update', $this->project);

    DB::transaction(function () use ($taskId, $position) {
        $ids = $this->project->tasks()->orderBy('position')->lockForUpdate()->pluck('id');

        abort_unless($ids->contains($taskId), 404);   // лише задачі цього проєкту

        $position = max(0, min($position, $ids->count() - 1));

        $ordered = $ids->reject(fn ($id) => $id === $taskId)->values();
        $ordered->splice($position, 0, [$taskId]);

        foreach ($ordered as $index => $id) {
            Task::whereKey($id)->update(['position' => $index]);
        }
    });

    unset($this->tasks);
}

Що тут важливо:

  • ідентифікатор і позиція - від клієнта. Без перевірки, що задача належить проєкту користувача, можна пересунути чужу задачу. Пошук через зв'язок ($this->project->tasks()) чи findOrFail в області видимості власника обов'язковий;
  • межі позиції - від'ємна чи завелика позиція не повинна ламати нумерацію;
  • перенумерація в транзакції з блокуванням - два користувачі, що одночасно сортують один список, інакше отримають дублікати позицій;
  • для довгих списків оновлювати лише зсунутий діапазон (increment/decrement для позицій між старою й новою), а не всі рядки;
  • wire:key на елементах обов'язковий - після відповіді сервера морфінг має зіставити елементи за ідентифікатором, а не за позицією;
  • для переміщення між колонками перевіряти й колонку призначення (третій параметр) - вона теж може бути чужою.

Альтернатива перенумерації - дробові чи «розріджені» позиції (крок 1000, вставка посередині між сусідами): одне оновлення замість багатьох, з періодичною перенумерацією.

Докладніше в документації: wire:sort

Схожі питання