From 25c87ef115cdbf14dafcb970966f8a815b2ae946 Mon Sep 17 00:00:00 2001 From: Brabli <67018167+Brabli@users.noreply.github.com> Date: Tue, 19 Jul 2022 10:21:40 +0100 Subject: [PATCH] First commit --- .gitignore | 5 ++ LICENSE | 1 + README.md | 1 + composer.json | 38 +++++++++++++ phpunit.xml.dist | 27 +++++++++ src/Entity/GeocodeData.php | 32 +++++++++++ src/Geocoder.php | 66 ++++++++++++++++++++++ src/Interface/MappableInterface.php | 18 ++++++ src/PcmGeocodeBundle.php | 9 +++ src/Trait/MappableTrait.php | 41 ++++++++++++++ tests/Service/GeocodeTest.php | 55 ++++++++++++++++++ tests/Trait/MappableTraitTest.php | 86 +++++++++++++++++++++++++++++ 12 files changed, 379 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 composer.json create mode 100644 phpunit.xml.dist create mode 100644 src/Entity/GeocodeData.php create mode 100644 src/Geocoder.php create mode 100644 src/Interface/MappableInterface.php create mode 100644 src/PcmGeocodeBundle.php create mode 100644 src/Trait/MappableTrait.php create mode 100644 tests/Service/GeocodeTest.php create mode 100644 tests/Trait/MappableTraitTest.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5bd7ab5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +.vscode +/vendor +composer.lock +.phpunit.result.cache \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2831d9e --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +License diff --git a/README.md b/README.md new file mode 100644 index 0000000..304360c --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Readme diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a8defa8 --- /dev/null +++ b/composer.json @@ -0,0 +1,38 @@ +{ + "name": "pcm/geocode-bundle", + "description": "Geocode postcodes and entities", + "type": "symfony-bundle", + "license": "MIT", + "authors": [ + { + "name": "Bradley Goode", + "email": "bg@pcmsystems.co.uk" + }, + { + "name": "Matt Feeney", + "email": "mf@pcmsystems.co.uk" + } + ], + "require": { + "php": "^8.1.0", + "symfony/http-client": "^6.1", + "symfony/dependency-injection": "^6.1" + }, + + "require-dev": { + "phpunit/phpunit": "^9.5", + "symfony/phpunit-bridge": "^6.1" + }, + + "autoload": { + "psr-4": { + "Pcm\\GeocodeBundle\\": "src/" + } + }, + + "autoload-dev": { + "psr-4": { + "Pcm\\GeocodeBundle\\Tests\\": "tests/" + } + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..ac5061e --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,27 @@ + + + + + + + + + + + + + ./tests + + + + + + ./src + + + \ No newline at end of file diff --git a/src/Entity/GeocodeData.php b/src/Entity/GeocodeData.php new file mode 100644 index 0000000..ab6bb6f --- /dev/null +++ b/src/Entity/GeocodeData.php @@ -0,0 +1,32 @@ +latitude = $latitude; + } + + public function setLongitude(float $longitude): void + { + $this->longitude = $longitude; + } + + public function getLatitude(): float + { + return $this->getLatitude(); + } + + public function getLongitude(): float + { + return $this->getLongitude(); + } +} diff --git a/src/Geocoder.php b/src/Geocoder.php new file mode 100644 index 0000000..cfdd0f6 --- /dev/null +++ b/src/Geocoder.php @@ -0,0 +1,66 @@ +getHttpClient(); + $response = $this->makeApiRequest($client, $postcode); + $data = $response->toArray(); + $this->throwIfNoResponseData($data); + + return $this->createGeocodeDataObject($data); + } + + private function getHttpClient(): HttpClientInterface + { + return HttpClient::create([ + 'headers' => ["User-Agent" => "Geocode"] + ]); + } + + private function makeApiRequest(HttpClientInterface $client, string $postcode): ResponseInterface + { + return $client->request( + method: 'GET', + url: self::API_URL, + options: [ + 'query' => [ + 'format' => 'json', + 'postalcode' => $postcode + ] + ] + ); + } + + private function throwIfNoResponseData(array $data): void + { + if (empty($data)) + throw new Exception("No data was received from API response! Were the arguments valid?"); + } + + private function createGeocodeDataObject(array $data): GeocodeData + { + $geocodeData = new GeocodeData(); + $geocodeData->setLatitude((float) $data[0]['lat']); + $geocodeData->setLongitude((float) $data[0]['lon']); + + return $geocodeData; + } +} diff --git a/src/Interface/MappableInterface.php b/src/Interface/MappableInterface.php new file mode 100644 index 0000000..e1f6596 --- /dev/null +++ b/src/Interface/MappableInterface.php @@ -0,0 +1,18 @@ +latitude; + } + + public function getLongitude(): ?float + { + return $this->longitude; + } + + public function setLatitude(float $lat): self + { + $this->latitude = $lat; + + return $this; + } + + public function setLongitude(float $lon): self + { + $this->longitude = $lon; + + return $this; + } + + public function isGeocoded(): bool + { + return $this->getLatitude() && $this->getLongitude(); + } +} diff --git a/tests/Service/GeocodeTest.php b/tests/Service/GeocodeTest.php new file mode 100644 index 0000000..3de4ad9 --- /dev/null +++ b/tests/Service/GeocodeTest.php @@ -0,0 +1,55 @@ +geocoder = new Geocoder(); + } + + public function testGeocodeInstance(): void + { + $this->assertInstanceOf(\Pcm\GeocodeBundle\Geocoder::class, $this->geocoder); + } + + public function testGeocodePostcodeThrowsOnInvalidInput(): void + { + sleep(1); + $this->expectException(\Exception::class); + $this->geocoder->geocodePostcode('aaaaaaaa'); + } + + public function testGeocodePostcodeReturnsGeocodeObject(): void + { + sleep(1); + $result = $this->geocoder->geocodePostcode(self::POSTCODE); + $this->assertInstanceOf(GeocodeData::class, $result); + } + + private function getMappableEntity(): MappableInterface + { + return new class implements MappableInterface + { + use MappableTrait; + }; + } + +} diff --git a/tests/Trait/MappableTraitTest.php b/tests/Trait/MappableTraitTest.php new file mode 100644 index 0000000..42545db --- /dev/null +++ b/tests/Trait/MappableTraitTest.php @@ -0,0 +1,86 @@ +obj = $this->getTraitObject(); + } + + public function testSetLatitude(): void + { + $this->assertInstanceOf(MappableInterface::class, $this->obj->setLatitude(self::FLOAT)); + } + + public function testGetLatitudeReturnsNull(): void + { + $this->assertNull($this->obj->getLatitude()); + } + + public function testGetLatitude(): void + { + $this->obj->setLatitude(self::FLOAT); + $this->assertSame(self::FLOAT, $this->obj->getLatitude()); + } + + public function testSetLongitude(): void + { + $this->assertInstanceOf(MappableInterface::class, $this->obj->setLongitude(self::FLOAT)); + } + + public function testGetLongitudeReturnsNull(): void + { + $this->assertNull($this->obj->getLongitude()); + } + + public function testGetLongitude(): void + { + $this->obj->setLongitude(self::FLOAT); + $this->assertSame(self::FLOAT, $this->obj->getLongitude()); + } + + public function testIsGeocodedReturnsFalse(): void + { + $this->assertFalse($this->obj->isGeocoded()); + } + + public function testIsGeocodedReturnsFalseIfLatIsSet(): void + { + $this->obj->setLatitude(self::FLOAT); + $this->assertFalse($this->obj->isGeocoded()); + } + + public function testIsGeocodedReturnsFalseIfLonIsSet(): void + { + $this->obj->setLongitude(self::FLOAT); + $this->assertFalse($this->obj->isGeocoded()); + } + + public function testIsGeocodedReturnsTrueIfLatAndLonAreSet(): void + { + $this->obj->setLatitude(self::FLOAT); + $this->obj->setLongitude(self::FLOAT); + $this->assertTrue($this->obj->isGeocoded()); + } + + private function getTraitObject(): MappableInterface + { + return new class implements MappableInterface + { + use MappableTrait; + }; + } + +}