diff --git a/app/Rules/BirthDateMatchSouthAfricanId.php b/app/Rules/BirthDateMatchSouthAfricanId.php new file mode 100644 index 0000000..18b5062 --- /dev/null +++ b/app/Rules/BirthDateMatchSouthAfricanId.php @@ -0,0 +1,81 @@ +. + +namespace Spoorsny\Laravel\Rules; + +use Carbon\Carbon; +use Closure; +use Illuminate\Contracts\Validation\DataAwareRule; +use Illuminate\Contracts\Validation\ValidationRule; +use Spoorsny\ValueObjects\SouthAfricanId; + +/** + * Rule that validates that a date string matches the date segment of a South African identity number. + * + * @see {@link https://laravel.com/docs/11.x/validation#using-rule-objects} + * + * @author Geoffrey Bernardo van Wyk + * @copyright 2024 Geoffrey Bernardo van Wyk {@link https://geoffreyvanwyk.dev} + * @license {@link http://www.gnu.org/copyleft/gpl.html} GNU GPL v3 or later + */ +class BirthDateMatchSouthAfricanId implements DataAwareRule, ValidationRule +{ + /** + * All of the data under validation. + * + * @var array + */ + protected array $data = []; + + /** + * Run the validation rule. + * + * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail + */ + public function validate(string $attribute, mixed $value, Closure $fail): void + { + if (! isset($this->data['south_african_id'])) { + return; + } + + try { + $southAfricanId = new SouthAfricanId($this->data['south_african_id']); + } catch (\Throwable $th) { + return; + } + + $birthDate = new Carbon($value); + + if ($birthDate->format('ymd') === $southAfricanId->dateSegment()) { + return; + } + + $fail('The :attribute field does not match the South African ID field.'); + } + + /** + * Set the data under validation. + * + * @param array $data + */ + public function setData(array $data): static + { + $this->data = $data; + + return $this; + } +} diff --git a/tests/Unit/BirthDateMatchSouthAfricanIdRuleTest.php b/tests/Unit/BirthDateMatchSouthAfricanIdRuleTest.php new file mode 100644 index 0000000..0ec311e --- /dev/null +++ b/tests/Unit/BirthDateMatchSouthAfricanIdRuleTest.php @@ -0,0 +1,61 @@ +. + +namespace Spoorsny\Laravel\Tests\Unit; + +use Illuminate\Support\Facades\Validator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use Spoorsny\Laravel\Rules\BirthDateMatchSouthAfricanId; +use Spoorsny\Laravel\Tests\TestCase; + +/** + * Unit test for validation rule \Spoorsny\Laravel\Rules\BirthDateMatchSouthAfricanId. + * + * @author Geoffrey Bernardo van Wyk + * @copyright 2024 Geoffrey Bernardo van Wyk {@link https://geoffreyvanwyk.dev} + * @license {@link http://www.gnu.org/copyleft/gpl.html} GNU GPL v3 or later + */ +#[CoversClass(BirthDateMatchSouthAfricanId::class)] +class BirthDateMatchSouthAfricanIdRuleTest extends TestCase +{ + #[Test] + public function it_passes_matching_date(): void + { + $validator = Validator::make([ + 'south_african_id' => '240620 3710 097', + 'birth_date' => '1924-06-20', + ], [ + 'birth_date' => [new BirthDateMatchSouthAfricanId], + ]); + + $this->assertTrue($validator->passes()); + } + + #[Test] + public function it_fails_nonmatching_date(): void + { + $validator = Validator::make([ + 'south_african_id' => '240620 3710 097', + 'birth_date' => '1925-06-20', + ], [ + 'birth_date' => [new BirthDateMatchSouthAfricanId], + ]); + + $this->assertTrue($validator->fails()); + } +}