feat(event): implement EventDateGroupingService to group historical dates and update related resources and tests
This commit is contained in:
101
app/Domains/Event/Services/EventDateGroupingService.php
Normal file
101
app/Domains/Event/Services/EventDateGroupingService.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user