format( 'w' ) - $startOfWeek + 7 ) % 7; return $anchor->modify( '-' . $shift . ' days' )->format( 'Y-m-d' ); } /** * Bucket slots into the seven days of the week starting at `$weekStart` * (`Y-m-d`). Every day is present, empty or not, in calendar order. * * @param list $slots * @return list}> */ public static function days( string $weekStart, array $slots ): array { $start = self::parseDay( $weekStart ) ?? new \DateTimeImmutable( 'today' ); $byDay = []; foreach ( $slots as $slot ) { $byDay[ substr( $slot->startDt, 0, 10 ) ][] = $slot; } $days = []; for ( $i = 0; $i < 7; $i++ ) { $date = $start->modify( '+' . $i . ' days' )->format( 'Y-m-d' ); $days[] = [ 'date' => $date, 'slots' => $byDay[ $date ] ?? [], ]; } return $days; } /** * Bucket arbitrary items into the seven days of the week starting at * `$weekStart` (`Y-m-d`), using `$dayOf` to extract each item's `Y-m-d` day. * Every day is present, empty or not, in calendar order. * * @template T * @param list $items * @param callable(T): string $dayOf * @return list}> */ public static function bucket( string $weekStart, array $items, callable $dayOf ): array { $start = self::parseDay( $weekStart ) ?? new \DateTimeImmutable( 'today' ); $byDay = []; foreach ( $items as $item ) { $byDay[ $dayOf( $item ) ][] = $item; } $days = []; for ( $i = 0; $i < 7; $i++ ) { $date = $start->modify( '+' . $i . ' days' )->format( 'Y-m-d' ); $days[] = [ 'date' => $date, 'items' => $byDay[ $date ] ?? [], ]; } return $days; } private static function parseDay( string $value ): ?\DateTimeImmutable { $day = \DateTimeImmutable::createFromFormat( '!Y-m-d', $value ); return false !== $day && $day->format( 'Y-m-d' ) === $value ? $day : null; } }