$dates * @return Collection */ 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 $rescheduled */ $rescheduled = $group['rescheduled']; $destination->setRelation( 'adminRescheduledDates', new EloquentCollection($rescheduled->sort($this->dateSorter())->values()->all()), ); return $destination; }) ->sort($this->dateSorter()) ->values(); } /** @param Collection $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(), ]; } }