Add functionality to change the order of models in the product list. This includes new actions, requests, and UI components for user interaction.
98 lines
3.1 KiB
PHP
98 lines
3.1 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 Webmozart\Assert\Assert;
|
|
|
|
/**
|
|
* @implements \App\Contracts\Action<\App\Actions\Common\ChangeModelOrderPositionInput, bool>
|
|
*/
|
|
final class ChangeModelOrderPosition implements Action
|
|
{
|
|
private const int MAX_ORDER_VALUE = 4294967295;
|
|
|
|
/**
|
|
* @param \App\Actions\Common\ChangeModelOrderPositionInput $input
|
|
*/
|
|
#[\Override]
|
|
public function __invoke(ActionInput $input): bool
|
|
{
|
|
Assert::isInstanceOf($input, ChangeModelOrderPositionInput::class);
|
|
|
|
$model = $input->model;
|
|
$currentOrder = $model->getOrder();
|
|
$targetOrder = $input->targetOrder;
|
|
|
|
if ($currentOrder === $targetOrder) {
|
|
return true;
|
|
}
|
|
|
|
/** @var Builder<Model&ModelWithOrder> $baseQuery */
|
|
$baseQuery = method_exists($model, 'withTrashed')
|
|
// @phpstan-ignore method.notFound (withTrashed)
|
|
? $model::query()->withTrashed()
|
|
: $model::query();
|
|
|
|
/** @var int|null $maxOrder */
|
|
$maxOrder = (clone $baseQuery)->max('order');
|
|
if ($maxOrder !== null && $targetOrder > $maxOrder) {
|
|
$targetOrder = (int) $maxOrder;
|
|
}
|
|
|
|
if ($currentOrder === $targetOrder) {
|
|
return true;
|
|
}
|
|
|
|
// Deactivate timestamps
|
|
$model->timestamps = false;
|
|
|
|
// Step 1: Temporarily park the moving model at MAX_ORDER_VALUE
|
|
$model->setOrder(self::MAX_ORDER_VALUE);
|
|
$model->saveQuietly();
|
|
|
|
if ($targetOrder < $currentOrder) {
|
|
// Moving UP
|
|
/** @var \Illuminate\Database\Eloquent\Collection<int, Model&ModelWithOrder> $itemsToShift */
|
|
$itemsToShift = (clone $baseQuery)
|
|
->where('order', '>=', $targetOrder)
|
|
->where('order', '<', $currentOrder)
|
|
->whereKeyNot($model->getKey())
|
|
->orderBy('order', 'desc')
|
|
->get();
|
|
|
|
foreach ($itemsToShift as $item) {
|
|
$item->timestamps = false;
|
|
$item->setOrder($item->getOrder() + 1);
|
|
$item->saveQuietly();
|
|
}
|
|
} else {
|
|
// Moving DOWN
|
|
/** @var \Illuminate\Database\Eloquent\Collection<int, Model&ModelWithOrder> $itemsToShift */
|
|
$itemsToShift = (clone $baseQuery)
|
|
->where('order', '>', $currentOrder)
|
|
->where('order', '<=', $targetOrder)
|
|
->whereKeyNot($model->getKey())
|
|
->orderBy('order', 'asc')
|
|
->get();
|
|
|
|
foreach ($itemsToShift as $item) {
|
|
$item->timestamps = false;
|
|
$item->setOrder($item->getOrder() - 1);
|
|
$item->saveQuietly();
|
|
}
|
|
}
|
|
|
|
// Step 2: Set the moving model to the target order
|
|
$model->setOrder($targetOrder);
|
|
$model->saveQuietly();
|
|
|
|
return true;
|
|
}
|
|
}
|