Skip to content

Commit fde468a

Browse files
norkunasNyholm
authored andcommitted
Add address validator constraint (#212)
1 parent f1435e8 commit fde468a

File tree

5 files changed

+192
-1
lines changed

5 files changed

+192
-1
lines changed

Resources/config/services.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ services:
1919
arguments: ['@Geocoder\ProviderAggregator']
2020
tags: [{ name: 'console.command' }]
2121

22+
Bazinga\GeocoderBundle\Validator\Constraint\AddressValidator: ['@geocoder']
23+
2224
# Keep these aliases for BC purpose
2325
bazinga_geocoder.geocoder:
2426
alias: "Geocoder\\ProviderAggregator"
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the BazingaGeocoderBundle package.
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*
10+
* @license MIT License
11+
*/
12+
13+
namespace Bazinga\GeocoderBundle\Tests\Validator\Constraint;
14+
15+
use Bazinga\GeocoderBundle\Validator\Constraint\Address;
16+
use Bazinga\GeocoderBundle\Validator\Constraint\AddressValidator;
17+
use Http\Client\Curl\Client;
18+
use Geocoder\Provider\Nominatim\Nominatim;
19+
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
20+
21+
class AddressValidatorTest extends ConstraintValidatorTestCase
22+
{
23+
protected function createValidator()
24+
{
25+
$geocoder = Nominatim::withOpenStreetMapServer(new Client(), 'BazingaGeocoderBundle/Test');
26+
27+
return new AddressValidator($geocoder);
28+
}
29+
30+
public function testNullIsValid()
31+
{
32+
$this->validator->validate(null, new Address());
33+
34+
$this->assertNoViolation();
35+
}
36+
37+
public function testEmptyStringIsValid()
38+
{
39+
$this->validator->validate('', new Address());
40+
41+
$this->assertNoViolation();
42+
}
43+
44+
/**
45+
* @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
46+
*/
47+
public function testExpectsStringCompatibleType()
48+
{
49+
$this->validator->validate(new \stdClass(), new Address());
50+
}
51+
52+
public function testValidAddress()
53+
{
54+
$this->validator->validate('Berlin, Germany', new Address());
55+
56+
$this->assertNoViolation();
57+
}
58+
59+
public function testInvalidAddress()
60+
{
61+
$address = 'Bifrost, Nine Realms';
62+
63+
$constraint = new Address([
64+
'message' => 'myMessage {{ address }}',
65+
]);
66+
67+
$this->validator->validate($address, $constraint);
68+
69+
$this->buildViolation('myMessage {{ address }}')
70+
->setParameter('{{ address }}', '"'.$address.'"')
71+
->setInvalidValue($address)
72+
->setCode(Address::INVALID_ADDRESS_ERROR)
73+
->assertRaised();
74+
}
75+
}

Validator/Constraint/Address.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the BazingaGeocoderBundle package.
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*
10+
* @license MIT License
11+
*/
12+
13+
namespace Bazinga\GeocoderBundle\Validator\Constraint;
14+
15+
use Symfony\Component\Validator\Constraint;
16+
17+
/**
18+
* @Annotation
19+
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
20+
*
21+
* @author Tomas Norkūnas <[email protected]>
22+
*/
23+
class Address extends Constraint
24+
{
25+
const INVALID_ADDRESS_ERROR = '2243aa07-2ea7-4eb7-962c-6a9586593f2c';
26+
27+
protected static $errorNames = [
28+
self::INVALID_ADDRESS_ERROR => 'INVALID_ADDRESS_ERROR',
29+
];
30+
31+
public $service = 'Bazinga\GeocoderBundle\Validator\Constraints\AddressValidator';
32+
33+
public $message = 'Address {{ address }} is not valid.';
34+
35+
public function validatedBy()
36+
{
37+
return $this->service;
38+
}
39+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the BazingaGeocoderBundle package.
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*
10+
* @license MIT License
11+
*/
12+
13+
namespace Bazinga\GeocoderBundle\Validator\Constraint;
14+
15+
use Geocoder\Exception\Exception;
16+
use Geocoder\Provider\Provider;
17+
use Geocoder\Query\GeocodeQuery;
18+
use Symfony\Component\Validator\Constraint;
19+
use Symfony\Component\Validator\ConstraintValidator;
20+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
21+
use Symfony\Component\Validator\Exception\UnexpectedValueException;
22+
23+
/**
24+
* @author Tomas Norkūnas <[email protected]>
25+
*/
26+
class AddressValidator extends ConstraintValidator
27+
{
28+
protected $addressGeocoder;
29+
30+
public function __construct(Provider $addressGeocoder)
31+
{
32+
$this->addressGeocoder = $addressGeocoder;
33+
}
34+
35+
public function validate($value, Constraint $constraint)
36+
{
37+
if (!$constraint instanceof Address) {
38+
throw new UnexpectedTypeException($constraint, Address::class);
39+
}
40+
41+
if (null === $value || '' === $value) {
42+
return;
43+
}
44+
45+
if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
46+
if (class_exists(UnexpectedValueException::class)) {
47+
throw new UnexpectedValueException($value, 'string');
48+
} else {
49+
throw new UnexpectedTypeException($value, 'string');
50+
}
51+
}
52+
53+
$value = (string) $value;
54+
55+
try {
56+
$collection = $this->addressGeocoder->geocodeQuery(GeocodeQuery::create($value));
57+
58+
if ($collection->isEmpty()) {
59+
$this->buildViolation($constraint, $value);
60+
}
61+
} catch (Exception $e) {
62+
$this->buildViolation($constraint, $value);
63+
}
64+
}
65+
66+
private function buildViolation(Address $constraint, string $address)
67+
{
68+
$this->context->buildViolation($constraint->message)
69+
->setParameter('{{ address }}', $this->formatValue($address))
70+
->setInvalidValue($address)
71+
->setCode(Address::INVALID_ADDRESS_ERROR)
72+
->addViolation();
73+
}
74+
}

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
"geocoder-php/nominatim-provider": "^5.0",
5252
"geocoder-php/maxmind-provider": "^4.0",
5353
"geocoder-php/mapzen-provider": "^4.0",
54-
"geocoder-php/mapquest-provider": "^4.0"
54+
"geocoder-php/mapquest-provider": "^4.0",
55+
"symfony/validator": "^3.3 || ^4.0"
5556
},
5657
"conflict": {
5758
"willdurand/geocoder": "3.2",

0 commit comments

Comments
 (0)