From 8d7abc85369a629b4b72b7f7d56174824e21ef4a Mon Sep 17 00:00:00 2001 From: Keith Solomon Date: Sun, 26 Apr 2026 14:29:53 -0500 Subject: [PATCH] feat: add url mapping value objects --- src/Url/UrlMapping.php | 32 +++++++++++++++++ src/Url/UrlMappingCollection.php | 30 ++++++++++++++++ tests/Unit/Url/UrlMappingCollectionTest.php | 38 +++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 src/Url/UrlMapping.php create mode 100644 src/Url/UrlMappingCollection.php create mode 100644 tests/Unit/Url/UrlMappingCollectionTest.php diff --git a/src/Url/UrlMapping.php b/src/Url/UrlMapping.php new file mode 100644 index 0000000..bb0048a --- /dev/null +++ b/src/Url/UrlMapping.php @@ -0,0 +1,32 @@ +normalizeUrl( $source_url ); + $destination_url = $this->normalizeUrl( $destination_url ); + + if ( '' === $source_url || '' === $destination_url ) { + throw new \InvalidArgumentException( 'Source and destination URLs are required.' ); + } + + $this->source_url = $source_url; + $this->destination_url = $destination_url; + } + + public function sourceUrl(): string { + return $this->source_url; + } + + public function destinationUrl(): string { + return $this->destination_url; + } + + private function normalizeUrl( string $url ): string { + return rtrim( trim( $url ), '/' ); + } +} diff --git a/src/Url/UrlMappingCollection.php b/src/Url/UrlMappingCollection.php new file mode 100644 index 0000000..60093e4 --- /dev/null +++ b/src/Url/UrlMappingCollection.php @@ -0,0 +1,30 @@ + + */ + private array $mappings; + + /** + * @param array $mappings URL mappings. + */ + public function __construct( array $mappings ) { + foreach ( $mappings as $mapping ) { + if ( ! $mapping instanceof UrlMapping ) { + throw new \InvalidArgumentException( 'URL mapping collections only accept UrlMapping instances.' ); + } + } + + $this->mappings = array_values( $mappings ); + } + + /** + * @return array + */ + public function all(): array { + return $this->mappings; + } +} diff --git a/tests/Unit/Url/UrlMappingCollectionTest.php b/tests/Unit/Url/UrlMappingCollectionTest.php new file mode 100644 index 0000000..c56a04d --- /dev/null +++ b/tests/Unit/Url/UrlMappingCollectionTest.php @@ -0,0 +1,38 @@ +sourceUrl() ); + self::assertSame( 'https://staging.example.test', $mapping->destinationUrl() ); + } + + public function test_it_rejects_empty_mapping_urls(): void { + $this->expectException( \InvalidArgumentException::class ); + $this->expectExceptionMessage( 'Source and destination URLs are required.' ); + + new UrlMapping( '', 'https://staging.example.test' ); + } + + public function test_it_returns_mappings_in_order(): void { + $first = new UrlMapping( 'https://example.test', 'https://staging.example.test' ); + $second = new UrlMapping( 'https://cdn.example.test', 'https://cdn.staging.example.test' ); + $collection = new UrlMappingCollection( array( $first, $second ) ); + + self::assertSame( array( $first, $second ), $collection->all() ); + } + + public function test_it_rejects_non_mapping_items(): void { + $this->expectException( \InvalidArgumentException::class ); + $this->expectExceptionMessage( 'URL mapping collections only accept UrlMapping instances.' ); + + new UrlMappingCollection( array( new \stdClass() ) ); + } +}