ldc-boutique-cnc-sable/app/Actions/Common/ChangeModelOrder.php
Jérémy Munsch c52b13cc51
feat(order): implement soft delete handling in order changes
Added support for soft-deleted models in ChangeModelOrder and ChangeModelOrderPosition actions.
This allows for proper order adjustments even when models are soft-deleted.
2026-10-29 19:04:46 +01:00

77 lines
2.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Actions\Common;
use App\Contracts\Action;
use App\Contracts\ActionInput;
use App\Interfaces\ModelWithOrder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use SortDirection;
use Webmozart\Assert\Assert;
/**
* @implements \App\Contracts\Action<\App\Actions\Common\ChangeModelOrderInput, bool>
*/
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<Model&ModelWithOrder> $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();
}
}