feat(services): add database storage for public holidays
This commit is contained in:
parent
24f36c16d0
commit
9b907cf25d
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\WorkingDays\PublicHolidays;
|
||||
|
||||
use App\Models\NonWorkingDays;
|
||||
|
||||
/**
|
||||
* Storage for storing public holidays in persistent storage.
|
||||
*/
|
||||
interface PublicHolidaysStateStorageInterface
|
||||
{
|
||||
/**
|
||||
* Store a public holiday in the storage.
|
||||
* @param \DateTimeImmutable $publicHolidayDate
|
||||
* @return NonWorkingDays|null
|
||||
*/
|
||||
public function storePublicHoliday(\DateTimeImmutable $publicHolidayDate): ?NonWorkingDays;
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\WorkingDays\PublicHolidays\Storage;
|
||||
|
||||
use App\Models\Country;
|
||||
use App\Models\NonWorkingDays;
|
||||
use Illuminate\Database\UniqueConstraintViolationException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DatabaseStorage implements PublicHolidaysStorageInterface
|
||||
{
|
||||
public function isPublicHoliday(string $countryCode, \DateTimeImmutable $holidayDate): bool
|
||||
{
|
||||
$country = $this->getCountryByCountryCode($countryCode);
|
||||
|
||||
return NonWorkingDays::where('country_id', $country->id)
|
||||
->where('non_working_date', $holidayDate->format('Y-m-d'))
|
||||
->exists();
|
||||
}
|
||||
|
||||
public function storePublicHoliday(string $countryCode, \DateTimeImmutable $holidayDate): ?NonWorkingDays
|
||||
{
|
||||
$country = $this->getCountryByCountryCode($countryCode);
|
||||
|
||||
try {
|
||||
return NonWorkingDays::create([
|
||||
'country_id' => $country->id,
|
||||
'non_working_date' => $holidayDate->format('Y-m-d'),
|
||||
]);
|
||||
} catch (UniqueConstraintViolationException $e) {
|
||||
// safe to ignore ... combination exists
|
||||
Log::warning("Non-working day already exists for country: " . $country . ", date: {$holidayDate->format('Y-m-d')}");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getPublicHolidaysInInterval(string $countryCode, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): array
|
||||
{
|
||||
$country = $this->getCountryByCountryCode($countryCode);
|
||||
return NonWorkingDays::where('country_id', $country->id)
|
||||
->whereBetween('non_working_date', [$startDate->format('Y-m-d'), $endDate->format('Y-m-d')])
|
||||
->get()->all();
|
||||
}
|
||||
|
||||
|
||||
public function countPublicHolidaysInInterval(string $countryCode, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): int
|
||||
{
|
||||
$country = $this->getCountryByCountryCode($countryCode);
|
||||
return NonWorkingDays::where('country_id', $country->id)
|
||||
->whereBetween('non_working_date', [$startDate->format('Y-m-d'), $endDate->format('Y-m-d')])
|
||||
->count();
|
||||
}
|
||||
|
||||
protected function getCountryByCountryCode(string $countryCode): Country
|
||||
{
|
||||
return Country::where('country_code', $countryCode)->first() ?? throw new \InvalidArgumentException("Country code '$countryCode' does not exist.");
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\WorkingDays\PublicHolidays\Storage;
|
||||
|
||||
use App\Models\NonWorkingDays;
|
||||
|
||||
/**
|
||||
* Interface for storing and retrieving public holidays independent on country.
|
||||
*/
|
||||
interface PublicHolidaysStorageInterface
|
||||
{
|
||||
/**
|
||||
* Determine if a given public holiday is in a given country.
|
||||
* @param string $countryCode
|
||||
* @param \DateTimeImmutable $holidayDate
|
||||
* @return bool
|
||||
*/
|
||||
public function isPublicHoliday(string $countryCode, \DateTimeImmutable $holidayDate): bool;
|
||||
|
||||
/**
|
||||
* Returns all public holidays in a given country within a given interval.
|
||||
* @param string $countryCode
|
||||
* @param \DateTimeImmutable $startDate
|
||||
* @param \DateTimeImmutable $endDate
|
||||
* @return NonWorkingDays[]
|
||||
*/
|
||||
public function getPublicHolidaysInInterval(string $countryCode, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): array;
|
||||
|
||||
/**
|
||||
* Counts all public holidays in a given country within a given interval.
|
||||
* @param string $countryCode
|
||||
* @param \DateTimeImmutable $startDate
|
||||
* @param \DateTimeImmutable $endDate
|
||||
* @return int
|
||||
*/
|
||||
public function countPublicHolidaysInInterval(string $countryCode, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): int;
|
||||
|
||||
/**
|
||||
* Stores a public holiday in the storage.
|
||||
* @param string $countryCode
|
||||
* @param \DateTimeImmutable $holidayDate
|
||||
* @return NonWorkingDays|null
|
||||
*/
|
||||
public function storePublicHoliday(string $countryCode, \DateTimeImmutable $holidayDate): ?NonWorkingDays;
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Services\WorkingDays\PublicHolidays\Storage;
|
||||
|
||||
use App\Models\Country;
|
||||
use App\Models\NonWorkingDays;
|
||||
use App\Services\PublicHolidays\Storage\DatabaseStoreTrait;
|
||||
use App\Services\PublicHolidays\Storage\NWDStore;
|
||||
use App\Services\WorkingDays\PublicHolidays\Storage\DatabaseStorage;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\TestCase;
|
||||
|
||||
class DatabaseStorageTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function testStoreWillStoreDate(): void
|
||||
{
|
||||
$country = Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']);
|
||||
$databaseStorage = new DatabaseStorage();
|
||||
$item1 = $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01'));
|
||||
$item2 = $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-02'));
|
||||
$this->assertEquals(2, NonWorkingDays::where('country_id', $country->id)->count());
|
||||
$this->assertEquals('2022-01-01', NonWorkingDays::where('id', $item1->id)->first()->non_working_date); // @phpstan-ignore property.nonObject
|
||||
$this->assertEquals('2022-01-02', NonWorkingDays::where('id', $item2->id)->first()->non_working_date); // @phpstan-ignore property.nonObject
|
||||
}
|
||||
|
||||
public function testStoreWillIgnoreDuplicateDates(): void
|
||||
{
|
||||
$country = Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']);
|
||||
$databaseStorage = new DatabaseStorage();
|
||||
$databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01'));
|
||||
$databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01'));
|
||||
$this->assertEquals(1, NonWorkingDays::where('country_id', $country->id)->count());
|
||||
}
|
||||
|
||||
public function testStoreWillStoreDateIfUsedAnotherCountry(): void
|
||||
{
|
||||
$country = Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']);
|
||||
$country2 = Country::create(['country_code' => 'YY', 'name' => 'second Czech republic']);
|
||||
$databaseStorage = new DatabaseStorage();
|
||||
$item1 = $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01'));
|
||||
$item2 = $databaseStorage->storePublicHoliday('YY', new \DateTimeImmutable('2022-01-01'));
|
||||
$this->assertEquals(1, NonWorkingDays::where('country_id', $country->id)->count());
|
||||
$this->assertEquals(1, NonWorkingDays::where('country_id', $country2->id)->count());
|
||||
$this->assertEquals('2022-01-01', NonWorkingDays::where('id', $item1->id)->first()->non_working_date); // @phpstan-ignore property.nonObject
|
||||
$this->assertEquals('2022-01-01', NonWorkingDays::where('id', $item2->id)->first()->non_working_date); // @phpstan-ignore property.nonObject
|
||||
}
|
||||
|
||||
public function testCountPublicHolidaysInIntervalWillReturnCorrectCount(): void
|
||||
{
|
||||
Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']);
|
||||
$databaseStorage = new DatabaseStorage();
|
||||
$databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01'));
|
||||
$databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-02'));
|
||||
$this->assertEquals(2, $databaseStorage->countPublicHolidaysInInterval('XX', new \DateTimeImmutable('2022-01-01'), new \DateTimeImmutable('2022-01-07')));
|
||||
}
|
||||
|
||||
public function testCountPublicHolidaysInIntervalWillReturnCorrectPublicHolidays(): void
|
||||
{
|
||||
Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']);
|
||||
$databaseStorage = new DatabaseStorage();
|
||||
$item1 = $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01'));
|
||||
$item2 = $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-02'));
|
||||
$holidays = $databaseStorage->getPublicHolidaysInInterval('XX', new \DateTimeImmutable('2022-01-01'), new \DateTimeImmutable('2022-01-07'));
|
||||
$this->assertCount(2, $holidays);
|
||||
$this->assertEquals($item1->id, $holidays[0]->id); // @phpstan-ignore property.nonObject
|
||||
$this->assertEquals($item2->id, $holidays[1]->id); // @phpstan-ignore property.nonObject
|
||||
}
|
||||
|
||||
public function testIsPublicHolidayWillReturnTrueIfDateIsPublicHoliday(): void
|
||||
{
|
||||
Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']);
|
||||
$databaseStorage = new DatabaseStorage();
|
||||
$databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01'));
|
||||
$this->assertTrue($databaseStorage->isPublicHoliday('XX', new \DateTimeImmutable('2022-01-01')));
|
||||
$this->assertFalse($databaseStorage->isPublicHoliday('XX', new \DateTimeImmutable('2022-01-02')));
|
||||
}
|
||||
|
||||
public function testStoreWillRaiseExceptionIfCountryCodeDoesNotExist(): void
|
||||
{
|
||||
$databaseStorage = new DatabaseStorage();
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$databaseStorage->storePublicHoliday('ZZ', new \DateTimeImmutable('2022-01-01'));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user