102 lines
3.1 KiB
PHP
102 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Domains\Event\Services;
|
|
|
|
use App\Domains\Event\Models\EventDate;
|
|
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
|
use Illuminate\Support\Collection;
|
|
|
|
class EventDateGroupingService
|
|
{
|
|
/**
|
|
* Groups every historical date under its final active destination.
|
|
*
|
|
* @param Collection<int, EventDate> $dates
|
|
* @return Collection<int, EventDate>
|
|
*/
|
|
public function group(Collection $dates): Collection
|
|
{
|
|
$byId = $dates->keyBy(fn (EventDate $date): int => (int) $date->getKey());
|
|
$groups = collect();
|
|
|
|
foreach ($dates as $date) {
|
|
$destination = $this->finalDestination($date, $byId);
|
|
$key = (int) $destination->getKey();
|
|
|
|
if (! $groups->has($key)) {
|
|
$groups->put($key, [
|
|
'destination' => $destination,
|
|
'rescheduled' => collect(),
|
|
]);
|
|
}
|
|
|
|
if (! $date->is($destination)) {
|
|
$group = $groups->get($key);
|
|
$historicalDate = clone $date;
|
|
$historicalDate->setAttribute(
|
|
'rescheduled_to_event_date_id',
|
|
$destination->getKey(),
|
|
);
|
|
$group['rescheduled']->push($historicalDate);
|
|
$groups->put($key, $group);
|
|
}
|
|
}
|
|
|
|
return $groups
|
|
->map(function (array $group): EventDate {
|
|
/** @var EventDate $destination */
|
|
$destination = clone $group['destination'];
|
|
/** @var Collection<int, EventDate> $rescheduled */
|
|
$rescheduled = $group['rescheduled'];
|
|
$destination->setRelation(
|
|
'adminRescheduledDates',
|
|
new EloquentCollection($rescheduled->sort($this->dateSorter())->values()->all()),
|
|
);
|
|
|
|
return $destination;
|
|
})
|
|
->sort($this->dateSorter())
|
|
->values();
|
|
}
|
|
|
|
/** @param Collection<int, EventDate> $byId */
|
|
private function finalDestination(EventDate $date, Collection $byId): EventDate
|
|
{
|
|
$current = $date;
|
|
$visited = collect();
|
|
|
|
while ($current->rescheduled_to_event_date_id !== null) {
|
|
$currentId = (int) $current->getKey();
|
|
|
|
if ($visited->contains($currentId)) {
|
|
break;
|
|
}
|
|
|
|
$visited->push($currentId);
|
|
$destination = $byId->get((int) $current->rescheduled_to_event_date_id);
|
|
|
|
if (! $destination instanceof EventDate) {
|
|
break;
|
|
}
|
|
|
|
$current = $destination;
|
|
}
|
|
|
|
return $current;
|
|
}
|
|
|
|
/** @return callable(EventDate, EventDate): int */
|
|
private function dateSorter(): callable
|
|
{
|
|
return fn (EventDate $left, EventDate $right): int => [
|
|
$left->date->format('Y-m-d'),
|
|
$left->time_start,
|
|
$left->getKey(),
|
|
] <=> [
|
|
$right->date->format('Y-m-d'),
|
|
$right->time_start,
|
|
$right->getKey(),
|
|
];
|
|
}
|
|
}
|