Files
thrillwiki_laravel/app/Livewire/ParkAreaReorderComponent.php

125 lines
3.6 KiB
PHP

<?php
namespace App\Livewire;
use App\Models\Park;
use App\Models\ParkArea;
use Livewire\Component;
use Illuminate\Support\Collection;
class ParkAreaReorderComponent extends Component
{
public Park $park;
public ?int $parentId = null;
public array $areas = [];
public function mount(Park $park, ?int $parentId = null): void
{
$this->park = $park;
$this->parentId = $parentId;
$this->loadAreas();
}
/**
* Load areas for the current context (either top-level or within a parent).
*/
protected function loadAreas(): void
{
$query = $this->park->areas()
->where('parent_id', $this->parentId)
->orderBy('position')
->select(['id', 'name', 'position', 'closing_date']);
$this->areas = $query->get()
->map(fn ($area) => [
'id' => $area->id,
'name' => $area->name,
'position' => $area->position,
'is_closed' => !is_null($area->closing_date),
'has_children' => $area->hasChildren(),
])
->toArray();
}
/**
* Handle reordering of areas.
*/
public function reorder(array $orderedIds): void
{
// Validate that all IDs belong to this park and parent context
$validIds = $this->park->areas()
->where('parent_id', $this->parentId)
->pluck('id')
->toArray();
$orderedIds = array_values(array_intersect($orderedIds, $validIds));
// Update positions
foreach ($orderedIds as $position => $id) {
ParkArea::where('id', $id)->update(['position' => $position]);
}
$this->loadAreas();
$this->dispatch('areas-reordered');
}
/**
* Move an area to a new parent.
*/
public function moveToParent(int $areaId, ?int $newParentId): void
{
$area = $this->park->areas()->findOrFail($areaId);
// Prevent circular references
if ($newParentId === $area->id) {
return;
}
// If moving to a new parent, validate it exists and belongs to this park
if ($newParentId) {
$newParent = $this->park->areas()->findOrFail($newParentId);
// Prevent moving to own descendant
$ancestorIds = Collection::make();
$current = $newParent;
while ($current) {
if ($ancestorIds->contains($current->id)) {
return; // Circular reference detected
}
$ancestorIds->push($current->id);
if ($current->id === $area->id) {
return; // Would create circular reference
}
$current = $current->parent;
}
}
// Get the next position in the new parent context
$maxPosition = $this->park->areas()
->where('parent_id', $newParentId)
->max('position');
$area->update([
'parent_id' => $newParentId,
'position' => ($maxPosition ?? -1) + 1,
]);
// Reorder the old parent's remaining areas to close gaps
$this->park->areas()
->where('parent_id', $this->parentId)
->where('position', '>', $area->position)
->decrement('position');
$this->loadAreas();
$this->dispatch('area-moved');
}
public function render()
{
return view('livewire.park-area-reorder-component', [
'parentArea' => $this->parentId
? $this->park->areas()->find($this->parentId)
: null,
]);
}
}