├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json ├── config ├── postcodes.php └── services.php ├── laravel-postal-code-validation.png └── src ├── Facades └── Postcode.php ├── PostcodesServiceProvider.php ├── Rules └── Postcode.php └── Service ├── BulkReverseGeocoding └── Geolocation.php └── PostcodeService.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [JustSteveKing] 4 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build-tests 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | test: 8 | runs-on: ${{ matrix.os }} 9 | 10 | strategy: 11 | matrix: 12 | php: [8.0, 8.1, '8.2'] 13 | laravel: ['9.*', '10.*', '11.*', '12.*'] 14 | dependency-version: [prefer-lowest, prefer-stable] 15 | os: [ubuntu-latest] 16 | include: 17 | - laravel: 10.* 18 | testbench: 8.* 19 | - laravel: 9.* 20 | testbench: 7.* 21 | - laravel: 11.* 22 | testbench: 9.* 23 | - laravel: 12.* 24 | testbench: 10.* 25 | exclude: 26 | - laravel: 11.* 27 | php: 8.0 28 | - laravel: 11.* 29 | php: 8.1 30 | - laravel: 12.* 31 | php: 8.0 32 | - laravel: 12.* 33 | php: 8.1 34 | 35 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ${{ matrix.os }} 36 | 37 | steps: 38 | - name: Checkout code 39 | uses: actions/checkout@v1 40 | 41 | - name: Cache dependencies 42 | uses: actions/cache@v1 43 | with: 44 | path: ~/.composer/cache/files 45 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 46 | 47 | - name: Setup PHP 48 | uses: shivammathur/setup-php@v2 49 | with: 50 | php-version: ${{ matrix.php }} 51 | extensions: xml, pcov, dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, soap, intl, gd, exif, iconv 52 | ini-values: post_max_size=256M 53 | tools: pecl 54 | coverage: none 55 | 56 | - name: Install dependencies 57 | run: | 58 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 59 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest 60 | 61 | - name: Execute tests 62 | run: vendor/bin/phpunit 63 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `LaravelPostcodes` will be documented in this file. 4 | 5 | Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles. 6 | 7 | ## v1.2.0 - 2020-03-03 8 | 9 | ### Added 10 | - A Pre validate option to validate, allowing regex matching to save service calls 11 | - A regex based outcode validation, this was a PR by PHPAdam that needed some work 12 | - Tests for both of the above 13 | - Laravel 7.* support 14 | 15 | ## v1.1.0 - 2019-10-25 16 | 17 | ### Added 18 | - Make tests more stringent 19 | - Unit tests make API calls 20 | - Update Postcode Service 21 | - Bulk lookup postcodes 22 | - Get nearest postcodes for a given longitude & latitude 23 | - Nearest postcodes for postcode 24 | - Autocomplete a postcode partial 25 | - Query for postcode 26 | - Lookup terminated postcode 27 | - Lookup Outward Code 28 | - Nearest outward code for outward code 29 | - Get nearest outward codes for a given longitude & latitude 30 | - Convert all returns to return Collection instead off Array where appropriate 31 | 32 | ### Deprecated 33 | - Nothing 34 | 35 | ### Fixed 36 | - Nothing 37 | 38 | ### Removed 39 | - Nothing 40 | 41 | ### Security 42 | - Nothing 43 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at `juststevemcd@gmail.com`. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/JustSteveKing/LaravelPostcodes). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - Check the code style with ``$ composer check-style`` and fix it with ``$ composer fix-style``. 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ composer test 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Steve McDougall 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | ![](laravel-postal-code-validation.png) 4 | 5 |

6 | 7 | # LaravelPostcodes 8 | 9 | [![Latest Version on Packagist][ico-version]][link-packagist] 10 | [![Software License][ico-license]](LICENSE.md) 11 | [![Build Status][ico-github-action]][link-github-action] 12 | [![Coverage Status][ico-scrutinizer]][link-scrutinizer] 13 | [![Quality Score][ico-code-quality]][link-code-quality] 14 | [![Total Downloads][ico-downloads]][link-downloads] 15 | 16 | A service wrapper around [postcodes.io](https://postcodes.io/) with validation rule and macro 17 | 18 | ## Install 19 | 20 | Via Composer 21 | 22 | ```bash 23 | $ composer require juststeveking/laravel-postcodes 24 | ``` 25 | 26 | After installation, merge configuration for services using: 27 | 28 | ```bash 29 | $ php artisan vendor:publish --provider="JustSteveKing\LaravelPostcodes\PostcodesServiceProvider" 30 | ``` 31 | 32 | If, for some reason, this doesn't work please use the following steps: 33 | 34 | - Add the following into the `config/services.php` configuration file: 35 | 36 | ```php 37 | [ 40 | 'url' => env('POSTCODES_URL', 'https://api.postcodes.io/') 41 | ], 42 | ``` 43 | 44 | - Add `POSTCODES_URL` to your `.env` file and add `https://api.postcodes.io/` as the value. 45 | 46 | 47 | ## Basic Usage 48 | 49 | You can use the validation rule: 50 | 51 | ``` php 52 | validate($request, [ 55 | 'postcode' => [ 56 | 'required', 57 | 'string', 58 | new Postcode(resolve(PostcodeService::class)) 59 | ] 60 | ]); 61 | ``` 62 | 63 | Or you can use the validation Macro: 64 | 65 | ```php 66 | $this->validate($request, [ 67 | 'postcode' => [ 68 | 'required', 69 | 'string', 70 | Rule::postcode() 71 | ] 72 | ]); 73 | ``` 74 | 75 | If you want to interact with the service itself: 76 | 77 | ```php 78 | postcodes = $service; 89 | } 90 | 91 | public function store(Request $request) 92 | { 93 | // validation using example above 94 | $location = $this->postcodes->getPostcode($request->postcode); 95 | } 96 | } 97 | ``` 98 | 99 | Or use the facade: 100 | 101 | ```php 102 | postcode); 110 | } 111 | } 112 | ``` 113 | 114 | ### Validate 115 | 116 | ```php 117 | validate('AB10 1AB'); 122 | 123 | // You can also use the facade: 124 | Postcode::validate('AB10 1AB'); 125 | ``` 126 | 127 | ### Validate Postcode 128 | 129 | ```php 130 | validate('AB10 1AB'); 135 | 136 | // You can also use the facade: 137 | Postcode::validate('AB10 1AB'); 138 | ``` 139 | 140 | ### Get Postcode information 141 | 142 | ```php 143 | getPostcode('AB10 1AB'); 148 | 149 | // You can also use the facade: 150 | Postcode::getPostcode('AB10 1AB'); 151 | ``` 152 | 153 | 154 | ### Bulk Lookup Postcodes 155 | 156 | ```php 157 | getPostcodes([ 162 | 'AB10 1AB', 163 | 'AB10 1AF', 164 | 'AB10 1AG', 165 | ]); 166 | 167 | // You can also use the facade: 168 | Postcode::getPostcodes([ 169 | 'AB10 1AB', 170 | 'AB10 1AF', 171 | 'AB10 1AG', 172 | ]); 173 | ``` 174 | 175 | ### Get nearest postcodes for a given longitude & latitude 176 | 177 | ```php 178 | nearestPostcodesForGivenLngAndLat( 183 | 0.629806, 184 | 51.792326 185 | ); 186 | 187 | // You can also use the facade: 188 | Postcode::nearestPostcodesForGivenLngAndLat( 189 | 0.629806, 190 | 51.792326 191 | ); 192 | ``` 193 | 194 | ### Nearest postcodes for postcode 195 | 196 | ```php 197 | nearest('AB10 1AB'); 202 | 203 | // You can also use the facade: 204 | Postcode::nearest('AB10 1AB'); 205 | ``` 206 | 207 | ### Autocomplete a postcode partial 208 | 209 | ```php 210 | autocomplete('AB10'); 215 | 216 | // You can also use the facade: 217 | Postcode::autocomplete('AB10'); 218 | ``` 219 | 220 | ### Query for postcode 221 | 222 | ```php 223 | query('AB10 1AB'); 228 | 229 | // You can also use the facade: 230 | Postcode::query('AB10 1AB'); 231 | ``` 232 | 233 | ### Lookup terminated postcode 234 | 235 | ```php 236 | getTerminatedPostcode('AB1 0AA'); 241 | 242 | // You can also use the facade: 243 | Postcode::getTerminatedPostcode('AB1 0AA'); 244 | ``` 245 | 246 | ### Lookup Outward Code 247 | 248 | ```php 249 | getOutwardCode('N11'); 254 | 255 | // You can also use the facade: 256 | Postcode::getOutwardCode('N11'); 257 | ``` 258 | 259 | ### Nearest outward code for outward code 260 | 261 | ```php 262 | getNearestOutwardCode('N11', $limit, $radius); 269 | 270 | // You can also use the facade: 271 | Postcode::getNearestOutwardCode('N11', $limit, $radius); 272 | ``` 273 | 274 | ### Get nearest outward codes for a given longitude & latitude 275 | 276 | ```php 277 | nearestOutwardCodesForGivenLngAndLat( 282 | 0.629806, 283 | 51.792326 284 | ); 285 | 286 | // You can also use the facade: 287 | Postcode::nearestOutwardCodesForGivenLngAndLat( 288 | 0.629806, 289 | 51.792326 290 | ); 291 | ``` 292 | 293 | ## Change log 294 | 295 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 296 | 297 | ## Testing 298 | 299 | ``` bash 300 | $ composer test 301 | ``` 302 | 303 | ## Contributing 304 | 305 | Please see [CONTRIBUTING](CONTRIBUTING.md) and [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) for details. 306 | 307 | ## Security 308 | 309 | If you discover any security related issues, please email juststevemcd@gmail.com instead of using the issue tracker. 310 | 311 | ## Credits 312 | 313 | - [Steve McDougall][link-author] 314 | - [All Contributors][link-contributors] 315 | - [Laravel News for the artwork](https://www.laravel-news.com) 316 | 317 | ## License 318 | 319 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 320 | 321 | [ico-version]: https://img.shields.io/packagist/v/juststeveking/laravel-postcodes.svg?style=flat-square 322 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 323 | [ico-github-action]: https://github.com/JustSteveKing/LaravelPostcodes/workflows/build-tests/badge.svg?branch=master 324 | [ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/JustSteveKing/LaravelPostcodes.svg?style=flat-square 325 | [ico-code-quality]: https://img.shields.io/scrutinizer/g/JustSteveKing/LaravelPostcodes.svg?style=flat-square 326 | [ico-downloads]: https://img.shields.io/packagist/dt/juststeveking/laravel-postcodes.svg?style=flat-square 327 | 328 | [link-packagist]: https://packagist.org/packages/juststeveking/laravel-postcodes 329 | [link-github-action]: https://github.com/JustSteveKing/LaravelPostcodes/actions 330 | [link-scrutinizer]: https://scrutinizer-ci.com/g/JustSteveKing/LaravelPostcodes/code-structure 331 | [link-code-quality]: https://scrutinizer-ci.com/g/JustSteveKing/LaravelPostcodes 332 | [link-downloads]: https://packagist.org/packages/juststeveking/laravel-postcodes 333 | [link-author]: https://github.com/JustSteveKing 334 | [link-contributors]: ../../contributors 335 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "juststeveking/laravel-postcodes", 3 | "type": "library", 4 | "description": "A service wrapper around postcodes.io", 5 | "keywords": [ 6 | "JustSteveKing", 7 | "LaravelPostcodes" 8 | ], 9 | "homepage": "https://github.com/JustSteveKing/LaravelPostcodes", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Steve McDougall", 14 | "email": "juststevemcd@gmail.com", 15 | "homepage": "https://github.com/JustSteveKing", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "php": "~7.2|^8.0", 21 | "guzzlehttp/guzzle": "^6.3|^7.0", 22 | "illuminate/support": "~5.8|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0" 23 | }, 24 | "require-dev": { 25 | "phpunit/phpunit": ">=7.0", 26 | "orchestra/testbench": "^4.0|^8.0|^9.0|^10.0", 27 | "squizlabs/php_codesniffer": "^3.0" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "JustSteveKing\\LaravelPostcodes\\": "src" 32 | } 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "JustSteveKing\\LaravelPostcodes\\": "tests" 37 | } 38 | }, 39 | "scripts": { 40 | "test:unit": "phpdbg -qrr ./vendor/bin/phpunit --coverage-html build/coverage-report -d memory_limit=512M --testdox", 41 | "test": [ 42 | "@test:unit" 43 | ], 44 | "check-style": "phpcs src tests", 45 | "fix-style": "phpcbf src tests" 46 | }, 47 | "extra": { 48 | "branch-alias": { 49 | "dev-master": "1.0-dev" 50 | }, 51 | "laravel": { 52 | "providers": [ 53 | "JustSteveKing\\LaravelPostcodes\\PostcodesServiceProvider" 54 | ], 55 | "aliases": { 56 | "Postcode": "JustSteveKing\\LaravelPostcodes\\Facades\\Postcode" 57 | } 58 | } 59 | }, 60 | "config": { 61 | "sort-packages": true 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /config/postcodes.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'postcode' => "#^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW]) ?[0-9][ABD-HJLNP-UW-Z]{2})$#", 6 | 'outcode' => "#^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW]))#" 7 | ] 8 | ]; 9 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'url' => env('POSTCODES_URL', 'https://api.postcodes.io/') 6 | ], 7 | ]; 8 | -------------------------------------------------------------------------------- /laravel-postal-code-validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustSteveKing/LaravelPostcodes/e6a68f68c8fd6e90c2f45ec3972a0f1b7da1de3b/laravel-postal-code-validation.png -------------------------------------------------------------------------------- /src/Facades/Postcode.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom( 18 | __DIR__ . '/../config/services.php', 19 | 'services' 20 | ); 21 | 22 | $this->mergeConfigFrom( 23 | __DIR__ . '/../config/postcodes.php', 24 | 'postcodes' 25 | ); 26 | 27 | $this->app->bind(PostcodeService::class, function () { 28 | return new PostcodeService( 29 | new Client() 30 | ); 31 | }); 32 | 33 | Rule::macro('postcode', function () { 34 | return new Postcode(resolve(PostcodeService::class)); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Rules/Postcode.php: -------------------------------------------------------------------------------- 1 | service = $service; 22 | } 23 | 24 | /** 25 | * Determine if the validation rule passes. 26 | * 27 | * @param string $attribute 28 | * @param mixed $value 29 | * @return bool 30 | */ 31 | public function passes($attribute, $value) 32 | { 33 | return $this->service->validate($value); 34 | } 35 | 36 | /** 37 | * Get the validation error message. 38 | * 39 | * @return string 40 | */ 41 | public function message() 42 | { 43 | return 'The submitted postcode is not a valid UK postcode'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Service/BulkReverseGeocoding/Geolocation.php: -------------------------------------------------------------------------------- 1 | lng = $lng; 25 | $this->lat = $lat; 26 | $this->radius = $radius; 27 | $this->limit = $limit; 28 | } 29 | 30 | public function getLng(): float 31 | { 32 | return $this->lng; 33 | } 34 | 35 | public function getLat(): float 36 | { 37 | return $this->lat; 38 | } 39 | 40 | public function getRadius(): ?int 41 | { 42 | return $this->radius; 43 | } 44 | 45 | public function getLimit(): ?int 46 | { 47 | return $this->limit; 48 | } 49 | 50 | public function toArray(): array 51 | { 52 | return [ 53 | 'longitude' => $this->getLng(), 54 | 'latitude' => $this->getLat(), 55 | ] + array_filter([ 56 | 'radius' => $this->getRadius(), 57 | 'limit' => $this->getLimit(), 58 | ]); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Service/PostcodeService.php: -------------------------------------------------------------------------------- 1 | url = config('services.postcodes.url'); 35 | 36 | $this->http = $client; 37 | } 38 | 39 | /** 40 | * Validate a postcode against the API 41 | * 42 | * @param string $postcode 43 | * @param bool $preValidate 44 | * 45 | * @return bool 46 | */ 47 | public function validate(string $postcode, bool $preValidate = false): bool 48 | { 49 | if ($preValidate) { 50 | return !!preg_match(config('postcodes.regex.postcode'), $postcode); 51 | } 52 | 53 | return $this->getResponse("postcodes/$postcode/validate"); 54 | } 55 | 56 | /** 57 | * Validate an Outcode using RegEx - not supported by the API 58 | * 59 | * @param string $postcode 60 | * 61 | * @return bool 62 | */ 63 | public function validateOutcode(string $postcode): bool 64 | { 65 | return !!preg_match(config('postcodes.regex.outcode'), $postcode); 66 | } 67 | 68 | /** 69 | * Get the address details from a postcode 70 | * 71 | * @param string $postcode 72 | * 73 | * @return object 74 | */ 75 | public function getPostcode(string $postcode): object 76 | { 77 | return $this->getResponse("postcodes/$postcode"); 78 | } 79 | 80 | /** 81 | * Get the address details from a multiple postcodes at once 82 | * 83 | * @param array $postcodes 84 | * 85 | * @param array $filter - optional array of fields to return 86 | * @return Collection 87 | * 88 | * @throws \GuzzleHttp\Exception\GuzzleException 89 | */ 90 | public function getPostcodes(array $postcodes, array $filter = []): Collection 91 | { 92 | $queryParams = ''; 93 | 94 | if (!empty($filter)) { 95 | $queryParams = Query::build(['filter' => implode(',', $filter)]); 96 | } 97 | 98 | return collect($this->getResponse( 99 | 'postcodes?' . $queryParams, 100 | 'POST', 101 | ['postcodes' => array_values($postcodes)] 102 | ))->map(function ($item) { 103 | return $item->result; 104 | }); 105 | } 106 | 107 | /** 108 | * Get information based on outward code including geo data 109 | * 110 | * @param string $outwardcode 111 | * 112 | * @return object 113 | */ 114 | public function getOutwardCode(string $outwardcode): object 115 | { 116 | return $this->getResponse("outcodes/$outwardcode"); 117 | } 118 | 119 | /** 120 | * Get the address details from a random postcode 121 | * 122 | * @return object 123 | */ 124 | public function getRandomPostcode() 125 | { 126 | return $this->getResponse('random/postcodes'); 127 | } 128 | 129 | /** 130 | * Query the API for a given string 131 | * 132 | * @param string $query 133 | * 134 | * @return Collection 135 | * 136 | * @throws \GuzzleHttp\Exception\GuzzleException 137 | */ 138 | public function query(string $query): Collection 139 | { 140 | $queryString = http_build_query(['q' => $query]); 141 | 142 | return collect($this->getResponse("postcodes?$queryString")); 143 | } 144 | 145 | /** 146 | * Get data for the postcodes nearest to the passed postcode 147 | * 148 | * @param string $postcode 149 | * 150 | * @return Collection 151 | * 152 | * @throws \GuzzleHttp\Exception\GuzzleException 153 | */ 154 | public function nearest(string $postcode): Collection 155 | { 156 | return collect($this->getResponse("postcodes/$postcode/nearest")); 157 | } 158 | 159 | /** 160 | * Lookup a terminated postcode. Returns the postcode, year and month of termination. 161 | * 162 | * @param string $postcode 163 | * 164 | * @return object 165 | */ 166 | public function getTerminatedPostcode($postcode) 167 | { 168 | return $this->getResponse("terminated_postcodes/$postcode"); 169 | } 170 | 171 | /** 172 | * Autocomplete a postcode partial. 173 | * 174 | * @param string $partialPostcode 175 | * 176 | * @return Collection 177 | * 178 | * @throws \GuzzleHttp\Exception\GuzzleException 179 | */ 180 | public function autocomplete(string $partialPostcode): Collection 181 | { 182 | return collect($this->getResponse("postcodes/$partialPostcode/autocomplete")); 183 | } 184 | 185 | /** 186 | * Get nearest outward codes for a given longitude & latitude 187 | * 188 | * @param float $longitude 189 | * 190 | * @param float $latitude 191 | * @return Collection 192 | * 193 | * @throws \GuzzleHttp\Exception\GuzzleException 194 | */ 195 | public function nearestOutwardCodesForGivenLngAndLat(float $longitude, float $latitude): Collection 196 | { 197 | return collect($this->getResponse(sprintf( 198 | 'outcodes?lon=%s&lat=%s', 199 | $longitude, 200 | $latitude 201 | ))); 202 | } 203 | 204 | /** 205 | * Get information about nearest outcodes based on outward code 206 | * 207 | * @param string $outwardcode 208 | * 209 | * @param int $limit Needs to be less than 100 210 | * @param int $radius Needs to be less than 25,000m 211 | * @return Collection 212 | * 213 | * @throws \GuzzleHttp\Exception\GuzzleException 214 | */ 215 | public function getNearestOutwardCode( 216 | string $outwardcode, 217 | int $limit = 10, 218 | int $radius = 5000 219 | ): Collection { 220 | $limit = ($limit > 100) ? 100 : $limit; 221 | $radius = ($radius > 100) ? 25000 : $radius; 222 | 223 | return collect($this->getResponse( 224 | "outcodes/$outwardcode/nearest?limit=$limit&radius=$radius" 225 | )); 226 | } 227 | 228 | /** 229 | * Get nearest postcodes for a given longitude & latitude 230 | * 231 | * @param float $longitude 232 | * @param float $latitude 233 | * 234 | * @return Collection 235 | * 236 | * @throws \GuzzleHttp\Exception\GuzzleException 237 | */ 238 | public function nearestPostcodesForGivenLngAndLat(float $longitude, float $latitude): Collection 239 | { 240 | return collect($this->getResponse(sprintf( 241 | 'postcodes?lon=%s&lat=%s', 242 | $longitude, 243 | $latitude 244 | ))); 245 | } 246 | 247 | /** 248 | * @param Geolocation[] $geolocations 249 | * 250 | * @return array|null 251 | * @throws GuzzleException 252 | */ 253 | public function bulkReverseGeocoding(array $geolocations): ?array 254 | { 255 | $body = json_encode(array_map(function (Geolocation $geolocation) { 256 | return $geolocation->toArray(); 257 | }, $geolocations)); 258 | return $this->getResponse('postcodes', 'POST', [], ['body' => $body]); 259 | } 260 | 261 | /** 262 | * Get the response and return the result object 263 | * 264 | * @param string|null $uri 265 | * @param string $method 266 | * @param array $data - data to be sent in post/patch/put request 267 | * @param array $options - array of options to be passed to curl, if $data is passed 'json' will be overwritten 268 | * @return mixed 269 | * @throws \GuzzleHttp\Exception\GuzzleException 270 | */ 271 | protected function getResponse( 272 | string $uri = null, 273 | string $method = 'GET', 274 | array $data = [], 275 | array $options = [] 276 | ) { 277 | $url = $this->url . $uri; 278 | 279 | if (!empty($data)) { 280 | $options['json'] = $data; 281 | } 282 | 283 | $request = $this->http->request( 284 | $method, 285 | $url, 286 | $options 287 | ); 288 | 289 | return json_decode($request->getBody()->getContents())->result; 290 | } 291 | } 292 | --------------------------------------------------------------------------------