*/ final class ChangeModelOrder implements Action { private const int MAX_ORDER_VALUE = 4294967295; /** * @param \App\Actions\Common\ChangeModelOrderInput $input */ #[\Override] public function __invoke(ActionInput $input): bool { Assert::isInstanceOf($input, ChangeModelOrderInput::class); $model = $input->model; $goUp = $input->direction === SortDirection::Ascending; $currentOrder = $model->getOrder(); $usesSoftDeletes = in_array(SoftDeletes::class, class_uses_recursive($model), true) || method_exists($model, 'trashed'); /** @var Builder $baseQuery */ $baseQuery = $usesSoftDeletes // @phpstan-ignore method.notFound (withTrashed) ? $model::query()->withTrashed() : $model::query(); /** @var (Model&ModelWithOrder)|null $adjacentModel */ $adjacentModel = $baseQuery ->where('order', $goUp ? '<' : '>', $currentOrder) ->orderBy('order', $goUp ? SortDirection::Descending : SortDirection::Ascending) ->first(); if ($adjacentModel === null) { return false; } $this->permuteModelOrders($model, $adjacentModel); return true; } private function permuteModelOrders(Model & ModelWithOrder $model1, Model & ModelWithOrder $model2): void { $order1 = $model1->getOrder(); $order2 = $model2->getOrder(); // Deactivate timestamps to avoid updating updated_at $model1->timestamps = false; $model2->timestamps = false; $model1->setOrder(self::MAX_ORDER_VALUE); $model1->saveQuietly(); $model2->setOrder($order1); $model2->saveQuietly(); $model1->setOrder($order2); $model1->saveQuietly(); } }