Added support for soft-deleted models in ChangeModelOrder and ChangeModelOrderPosition actions. This allows for proper order adjustments even when models are soft-deleted.
77 lines
2.2 KiB
PHP
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();
|
|
}
|
|
}
|