From 5d80abc2ff5d2be9e6b86518fd0f6a2c48fea5bd Mon Sep 17 00:00:00 2001 From: Mariano Goldman Date: Sun, 16 Feb 2025 12:09:40 -0300 Subject: [PATCH] All interfaces take and return immutable carbon dates and periods --- src/Agenda/SingleDateRangeAgenda.php | 26 +++++++++++----------- src/Agenda/WeeklyScheduleAgenda.php | 27 ++++++++++++----------- src/Contracts/Agenda.php | 4 ++-- src/Contracts/TimeBookable.php | 7 +++--- src/Contracts/TimeSlotter.php | 9 ++++---- src/Slots/AgendaSlotter.php | 21 +++++++++--------- src/Slots/DaySlotter.php | 20 ++++++++--------- src/WeeklySchedule.php | 4 ++-- tests/Agenda/WeeklyScheduleAgendaTest.php | 10 ++++----- tests/Slots/AgendaSlotterTest.php | 6 ++--- tests/Slots/DaySlotterTest.php | 6 ++--- tests/WeeklyScheduleUnitTest.php | 6 ++--- 12 files changed, 75 insertions(+), 71 deletions(-) diff --git a/src/Agenda/SingleDateRangeAgenda.php b/src/Agenda/SingleDateRangeAgenda.php index 3e89be6..668c653 100644 --- a/src/Agenda/SingleDateRangeAgenda.php +++ b/src/Agenda/SingleDateRangeAgenda.php @@ -4,39 +4,39 @@ namespace Puntodev\Bookables\Agenda; -use Carbon\Carbon; +use Carbon\CarbonInterface; use Exception; use League\Period\Period; use Puntodev\Bookables\Contracts\Agenda; class SingleDateRangeAgenda implements Agenda { - private Carbon $start; + private CarbonInterface $start; - private Carbon $end; + private CarbonInterface $end; /** * SingleDateRangeAgenda constructor. * - * @param Carbon $start - * @param Carbon $end + * @param CarbonInterface $start + * @param CarbonInterface $end */ - public function __construct(Carbon $start, Carbon $end) + public function __construct(CarbonInterface $start, CarbonInterface $end) { - $this->start = $start->clone(); - $this->end = $end->clone(); + $this->start = $start->toImmutable(); + $this->end = $end->toImmutable(); } /** - * @param Carbon $from - * @param Carbon $to + * @param CarbonInterface $from + * @param CarbonInterface $to * @return array|Period[] * @throws Exception */ - public function possibleRanges(Carbon $from, Carbon $to): array + public function possibleRanges(CarbonInterface $from, CarbonInterface $to): array { - $maxStart = $from->max($this->start); - $minEnd = $to->min($this->end); + $maxStart = $from->toImmutable()->max($this->start); + $minEnd = $to->toImmutable()->min($this->end); if ($maxStart->isAfter($minEnd)) { return []; } diff --git a/src/Agenda/WeeklyScheduleAgenda.php b/src/Agenda/WeeklyScheduleAgenda.php index c727f4d..2eb6e00 100644 --- a/src/Agenda/WeeklyScheduleAgenda.php +++ b/src/Agenda/WeeklyScheduleAgenda.php @@ -2,9 +2,10 @@ namespace Puntodev\Bookables\Agenda; -use Carbon\Carbon; -use DateInterval; -use DatePeriod; +use Carbon\CarbonImmutable; +use Carbon\CarbonInterface; +use Carbon\CarbonInterval; +use Carbon\CarbonPeriodImmutable; use Exception; use League\Period\Period; use Puntodev\Bookables\Contracts\Agenda; @@ -20,28 +21,28 @@ public function __construct(WeeklySchedule $weeklySchedule) } /** - * @param Carbon $from - * @param Carbon $to + * @param CarbonInterface $from + * @param CarbonInterface $to * @return array * @throws Exception */ - public function possibleRanges(Carbon $from, Carbon $to): array + public function possibleRanges(CarbonInterface $from, CarbonInterface $to): array { - $startOfDateFrom = $from->clone()->startOfDay(); - $endOfDateTo = $to->clone()->endOfDay(); + $startOfDateFrom = $from->toImmutable()->startOfDay(); + $endOfDateTo = $to->toImmutable()->endOfDay(); - $period = new DatePeriod( + $period = new CarbonPeriodImmutable( $startOfDateFrom, - new DateInterval('P1D'), + new CarbonInterval('P1D'), $endOfDateTo, ); $ret = []; foreach ($period as $date) { - $carbon = Carbon::instance($date); + $carbon = CarbonImmutable::instance($date); foreach ($this->weeklySchedule->forDate($carbon) as $range) { - $periodStart = $startOfDateFrom->max($carbon->clone()->setTimeFromTimeString($range['start'])); - $periodEnd = $endOfDateTo->min($carbon->clone()->setTimeFromTimeString($range['end'])); + $periodStart = $startOfDateFrom->max($carbon->setTimeFromTimeString($range['start'])); + $periodEnd = $endOfDateTo->min($carbon->setTimeFromTimeString($range['end'])); if ($periodEnd->isAfter($periodStart)) { $ret[] = Period::fromDate($periodStart, $periodEnd); } diff --git a/src/Contracts/Agenda.php b/src/Contracts/Agenda.php index b4d9901..abe2f2e 100644 --- a/src/Contracts/Agenda.php +++ b/src/Contracts/Agenda.php @@ -4,9 +4,9 @@ namespace Puntodev\Bookables\Contracts; -use Carbon\Carbon; +use Carbon\CarbonInterface; interface Agenda { - public function possibleRanges(Carbon $from, Carbon $to): array; + public function possibleRanges(CarbonInterface $from, CarbonInterface $to): array; } diff --git a/src/Contracts/TimeBookable.php b/src/Contracts/TimeBookable.php index fad2f0a..90122aa 100644 --- a/src/Contracts/TimeBookable.php +++ b/src/Contracts/TimeBookable.php @@ -4,11 +4,12 @@ namespace Puntodev\Bookables\Contracts; -use Carbon\Carbon; + +use Carbon\CarbonInterface; interface TimeBookable { - public function available(Carbon $start, Carbon $end): array; + public function available(CarbonInterface $start, CarbonInterface $end): array; - public function unavailable(Carbon $start, Carbon $end): array; + public function unavailable(CarbonInterface $start, CarbonInterface $end): array; } diff --git a/src/Contracts/TimeSlotter.php b/src/Contracts/TimeSlotter.php index 1416b47..7068780 100644 --- a/src/Contracts/TimeSlotter.php +++ b/src/Contracts/TimeSlotter.php @@ -2,15 +2,16 @@ namespace Puntodev\Bookables\Contracts; -use Carbon\Carbon; + +use Carbon\CarbonInterface; interface TimeSlotter { /** * Creates time slots for dates in the range between $startDate and $endDate - * @param Carbon $startDate - * @param Carbon $endDate + * @param CarbonInterface $startDate + * @param CarbonInterface $endDate * @return array */ - public function makeSlotsForDates(Carbon $startDate, Carbon $endDate): array; + public function makeSlotsForDates(CarbonInterface $startDate, CarbonInterface $endDate): array; } diff --git a/src/Slots/AgendaSlotter.php b/src/Slots/AgendaSlotter.php index eb44a71..e30aa4f 100644 --- a/src/Slots/AgendaSlotter.php +++ b/src/Slots/AgendaSlotter.php @@ -4,26 +4,27 @@ namespace Puntodev\Bookables\Slots; -use Carbon\Carbon; +use Carbon\CarbonImmutable; +use Carbon\CarbonInterface; use Carbon\CarbonInterval; -use Carbon\CarbonPeriod; +use Carbon\CarbonPeriodImmutable; use League\Period\Period; use Puntodev\Bookables\Contracts\Agenda; use Puntodev\Bookables\Contracts\TimeSlotter; /** - * TimeSlotter that uses an $agenda to obtain the date ranges for the required dates + * TimeSlotter that uses an $agenda to get the date ranges for the required dates * and creates slots for those ranges using a particular $duration. - * If $timeBefore is specified it ensures that before each appointment there's at least $timeBefore minutes. - * If $timeAfter is specified it ensures that after each appointment there's at least $timeAfter minutes. + * If $timeBefore is specified, it ensures that before each appointment there's at least $timeBefore minutes. + * If $timeAfter is specified, it ensures that after each appointment there's at least $timeAfter minutes. */ -class AgendaSlotter implements TimeSlotter +readonly class AgendaSlotter implements TimeSlotter { public function __construct(private Agenda $agenda, private int $duration, private int $timeAfter = 0, private int $timeBefore = 0) { } - public function makeSlotsForDates(Carbon $startDate, Carbon $endDate): array + public function makeSlotsForDates(CarbonInterface $startDate, CarbonInterface $endDate): array { $ranges = $this->agenda->possibleRanges($startDate, $endDate); @@ -33,10 +34,10 @@ public function makeSlotsForDates(Carbon $startDate, Carbon $endDate): array /** @var Period $range */ foreach ($ranges as $range) { - $dateRange = new CarbonPeriod( - Carbon::instance($range->startDate), + $dateRange = new CarbonPeriodImmutable( + CarbonImmutable::instance($range->startDate), new CarbonInterval("PT{$interval}M"), - Carbon::instance($range->endDate)->subMinutes($this->duration), + CarbonImmutable::instance($range->endDate)->subMinutes($this->duration), ); foreach ($dateRange as $slot) { diff --git a/src/Slots/DaySlotter.php b/src/Slots/DaySlotter.php index c0d730b..7e9c6ef 100644 --- a/src/Slots/DaySlotter.php +++ b/src/Slots/DaySlotter.php @@ -4,9 +4,9 @@ namespace Puntodev\Bookables\Slots; -use Carbon\Carbon; +use Carbon\CarbonInterface; use Carbon\CarbonInterval; -use Carbon\CarbonPeriod; +use Carbon\CarbonPeriodImmutable; use League\Period\Period; use Puntodev\Bookables\Contracts\TimeSlotter; @@ -14,30 +14,30 @@ * TimeSlotter that creates slots for the whole day for each date within the requested range * and creates slots for those dates using a particular $duration and $step. */ -class DaySlotter implements TimeSlotter +readonly class DaySlotter implements TimeSlotter { public function __construct(private int $duration, private int $step) { } - public function makeSlotsForDates(Carbon $startDate, Carbon $endDate): array + public function makeSlotsForDates(CarbonInterface $startDate, CarbonInterface $endDate): array { - $startOfDateFrom = $startDate->clone()->startOfDay(); - $endOfDateTo = $endDate->clone()->endOfDay(); + $startOfDateFrom = $startDate->toImmutable()->startOfDay(); + $endOfDateTo = $endDate->toImmutable()->endOfDay(); - $period = new CarbonPeriod( + $period = new CarbonPeriodImmutable( $startOfDateFrom, new CarbonInterval('P1D'), $endOfDateTo, ); $ret = []; - /** @var Carbon $date */ + /** @var CarbonInterface $date */ foreach ($period as $date) { - $dateRange = new CarbonPeriod( + $dateRange = new CarbonPeriodImmutable( $date, new CarbonInterval("PT{$this->step}M"), - $date->clone()->addDay()->subMinutes($this->duration), + $date->addDay()->subMinutes($this->duration), ); foreach ($dateRange as $slot) { diff --git a/src/WeeklySchedule.php b/src/WeeklySchedule.php index 7dc5233..47b3a2e 100644 --- a/src/WeeklySchedule.php +++ b/src/WeeklySchedule.php @@ -2,7 +2,7 @@ namespace Puntodev\Bookables; -use Carbon\Carbon; +use Carbon\CarbonInterface; use DateTime; use Exception; @@ -115,7 +115,7 @@ private static function validate(array $array): void } } - public function forDate(Carbon $date): array + public function forDate(CarbonInterface $date): array { return $this->forDay(self::$dowMap[$date->dayOfWeek]); } diff --git a/tests/Agenda/WeeklyScheduleAgendaTest.php b/tests/Agenda/WeeklyScheduleAgendaTest.php index 0766517..ba3824b 100644 --- a/tests/Agenda/WeeklyScheduleAgendaTest.php +++ b/tests/Agenda/WeeklyScheduleAgendaTest.php @@ -2,7 +2,7 @@ namespace Tests\Agenda; -use Carbon\Carbon; +use Carbon\CarbonImmutable; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use Puntodev\Bookables\Agenda\WeeklyScheduleAgenda; @@ -19,8 +19,8 @@ public function possibleRanges(): void $agenda = new WeeklyScheduleAgenda(WeeklySchedule::fromArray(WeeklySchedule::defaultWorkingHours())); $result = $agenda->possibleRanges( - Carbon::parse("2020-01-19"), - Carbon::parse("2020-01-31"), + CarbonImmutable::parse("2020-01-19"), + CarbonImmutable::parse("2020-01-31"), ); $this->assertRanges([ @@ -55,8 +55,8 @@ public function possibleRangesWithTimeZone(): void $tz = "Pacific/Auckland"; $result = $agenda->possibleRanges( - Carbon::parse("2020-01-19", $tz), - Carbon::parse("2020-01-31", $tz), + CarbonImmutable::parse("2020-01-19", $tz), + CarbonImmutable::parse("2020-01-31", $tz), ); $this->assertRanges([ diff --git a/tests/Slots/AgendaSlotterTest.php b/tests/Slots/AgendaSlotterTest.php index 6b6ecb3..408e466 100644 --- a/tests/Slots/AgendaSlotterTest.php +++ b/tests/Slots/AgendaSlotterTest.php @@ -2,7 +2,7 @@ namespace Tests\Slots; -use Carbon\Carbon; +use Carbon\CarbonImmutable; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; @@ -32,8 +32,8 @@ public function checkForDurationAndStepping($duration, $timeAfter, $timeBefore, $slotter = new AgendaSlotter($this->agenda, $duration, $timeAfter, $timeBefore); $result = $slotter->makeSlotsForDates( - Carbon::parse('2020-01-23'), - Carbon::parse('2020-01-25'), + CarbonImmutable::parse('2020-01-23'), + CarbonImmutable::parse('2020-01-25'), ); $this->assertRanges($expected, $result); diff --git a/tests/Slots/DaySlotterTest.php b/tests/Slots/DaySlotterTest.php index d0abb31..e8cece1 100644 --- a/tests/Slots/DaySlotterTest.php +++ b/tests/Slots/DaySlotterTest.php @@ -2,7 +2,7 @@ namespace Tests\Slots; -use Carbon\Carbon; +use Carbon\CarbonImmutable; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; @@ -20,8 +20,8 @@ public function checkForDurationAndStepping($duration, $stepping, $expected): vo $slotter = new DaySlotter($duration, $stepping); $result = $slotter->makeSlotsForDates( - Carbon::parse('2020-01-23'), - Carbon::parse('2020-01-23'), + CarbonImmutable::parse('2020-01-23'), + CarbonImmutable::parse('2020-01-23'), ); $this->assertRanges($expected, $result); diff --git a/tests/WeeklyScheduleUnitTest.php b/tests/WeeklyScheduleUnitTest.php index 963a429..a815c68 100644 --- a/tests/WeeklyScheduleUnitTest.php +++ b/tests/WeeklyScheduleUnitTest.php @@ -2,7 +2,7 @@ namespace Tests; -use Carbon\Carbon; +use Carbon\CarbonImmutable; use Exception; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; @@ -219,7 +219,7 @@ public function forDate(): void $weeklySchedule = WeeklySchedule::fromJson('{"daily": {"Sun":[{"start": "14:00", "end": "15:00"}]}, "hours_in_advance": 24}'); $this->assertArrayHasKey('Sun', $weeklySchedule->daily()); - $forSunday = $weeklySchedule->forDate(Carbon::parse('2020-06-14')); + $forSunday = $weeklySchedule->forDate(CarbonImmutable::parse('2020-06-14')); $this->assertArrayHasKey('start', $forSunday[0]); $this->assertArrayHasKey('end', $forSunday[0]); $this->assertEquals('14:00', $forSunday[0]['start']); @@ -263,7 +263,7 @@ public function forDateDisableAll(): void $weeklySchedule = WeeklySchedule::fromJson('{"daily": {"Sun":[{"start": "14:00", "end": "15:00"}]}, "hours_in_advance": 24, "disable_all": true}'); $this->assertArrayHasKey('Sun', $weeklySchedule->daily()); - $forSunday = $weeklySchedule->forDate(Carbon::parse('2020-06-14')); + $forSunday = $weeklySchedule->forDate(CarbonImmutable::parse('2020-06-14')); $this->assertCount(0, $forSunday); $this->assertEquals(24, $weeklySchedule->hoursInAdvance()); $this->assertTrue($weeklySchedule->disableAll());