Skip to content

Commit bb2322b

Browse files
authored
Merge pull request #391 from norkunas/auto-orm-listener
Auto register doctrine services, and support multiple metadata drivers
2 parents 3d6bbad + c8350b8 commit bb2322b

23 files changed

+190
-85
lines changed

config/orm.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
6+
7+
use Bazinga\GeocoderBundle\Doctrine\ORM\GeocodeEntityListener;
8+
use Bazinga\GeocoderBundle\Mapping\Driver\DriverInterface;
9+
10+
return static function (ContainerConfigurator $container) {
11+
$services = $container->services();
12+
13+
$services
14+
->set(GeocodeEntityListener::class)
15+
->args([
16+
tagged_locator('bazinga_geocoder.provider'),
17+
service(DriverInterface::class),
18+
])
19+
->tag('doctrine.event_listener', ['event' => 'onFlush'])
20+
;
21+
};

config/services.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
66

77
use Bazinga\GeocoderBundle\Command\GeocodeCommand;
8+
use Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver;
9+
use Bazinga\GeocoderBundle\Mapping\Driver\ChainDriver;
10+
use Bazinga\GeocoderBundle\Mapping\Driver\DriverInterface;
811
use Bazinga\GeocoderBundle\Plugin\FakeIpPlugin;
912
use Bazinga\GeocoderBundle\Validator\Constraint\AddressValidator;
1013
use Geocoder\Dumper\Dumper;
@@ -49,5 +52,15 @@
4952
service(ProviderAggregator::class),
5053
])
5154
->tag('validator.constraint_validator')
55+
56+
->set(ChainDriver::class)
57+
->args([
58+
tagged_iterator('bazinga_geocoder.metadata.driver', exclude: [ChainDriver::class]),
59+
])
60+
->tag('bazinga_geocoder.metadata.driver')
61+
->alias(DriverInterface::class, ChainDriver::class)
62+
63+
->set(AttributeDriver::class)
64+
->tag('bazinga_geocoder.metadata.driver')
5265
;
5366
};

doc/doctrine.md

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
# Doctrine annotation support
1+
# Doctrine support
22

33
*[<< Back to documentation index](/doc/index.md)*
44

5-
Wouldn't it be great if you could automatically save the coordinates of a users
6-
address every time it is updated? Wait not more here is the feature you been always
7-
wanted.
5+
Wouldn't it be great if you could automatically save the coordinates of a user's
6+
address every time it is updated? Well, wait no more—here is the feature you've
7+
always wanted!
88

99
First of all, update your entity:
1010

1111
```php
1212

1313
use Bazinga\GeocoderBundle\Mapping\Attributes as Geocoder;
1414

15-
#[Geocoder\Geocodeable()]
15+
#[Geocoder\Geocodeable(provider: 'acme')]
1616
class User
1717
{
1818
#[Geocoder\Address()]
@@ -32,7 +32,7 @@ Instead of annotating a property, you can also annotate a getter:
3232

3333
use Bazinga\GeocoderBundle\Mapping\Attributes as Geocoder;
3434

35-
#[Geocoder\Geocodeable()]
35+
#[Geocoder\Geocodeable(provider: 'acme')]
3636
class User
3737
{
3838
#[Geocoder\Latitude()]
@@ -42,36 +42,28 @@ class User
4242
private $longitude;
4343

4444
#[Geocoder\Address()]
45-
public function getAddress(): string
45+
public function getAddress(): \Stringable|string
4646
{
4747
// Your code...
4848
}
4949
}
5050
```
5151

52-
Secondly, register the Doctrine event listener and its dependencies in your `config/services.yaml` or `config/services.php` file.
53-
You have to indicate which provider to use to reverse geocode the address. Here we use `acme` provider we declared in bazinga_geocoder configuration earlier.
52+
Secondly, enable Doctrine ORM listener in the configuration:
5453

5554
```yaml
56-
Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver: ~
57-
58-
Bazinga\GeocoderBundle\Doctrine\ORM\GeocoderListener:
59-
class: Bazinga\GeocoderBundle\Doctrine\ORM\GeocoderListener
60-
arguments:
61-
- '@bazinga_geocoder.provider.acme'
62-
- '@Bazinga\GeocoderBundle\Mapping\Driver\AttributeDriver'
63-
tags:
64-
- { name: doctrine.event_listener, event: onFlush }
55+
bazinga_geocoder:
56+
orm:
57+
enabled: true
6558
```
6659
67-
It is done!
68-
Now you can use it:
60+
That's it! Now you can use it:
6961
7062
```php
7163
$user = new User();
7264
$user->setAddress('Brandenburger Tor, Pariser Platz, Berlin');
7365

74-
$em->persist($event);
66+
$em->persist($user);
7567
$em->flush();
7668

7769
echo $user->getLatitude(); // will output 52.516325

phpstan-baseline.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@
129129
'count' => 1,
130130
'path' => __DIR__.'/src/DependencyInjection/BazingaGeocoderExtension.php',
131131
];
132+
$ignoreErrors[] = [
133+
// identifier: argument.type
134+
'message' => '#^Parameter \\#2 \\$array of function array_key_exists expects array, array\\|bool\\|float\\|int\\|string\\|UnitEnum\\|null given\\.$#',
135+
'count' => 1,
136+
'path' => __DIR__.'/src/DependencyInjection/BazingaGeocoderExtension.php',
137+
];
132138
$ignoreErrors[] = [
133139
// identifier: argument.type
134140
'message' => '#^Parameter \\#2 \\$config of method Bazinga\\\\GeocoderBundle\\\\DependencyInjection\\\\BazingaGeocoderExtension\\:\\:configureProviderPlugins\\(\\) expects array, mixed given\\.$#',
@@ -151,13 +157,19 @@
151157
// identifier: method.nonObject
152158
'message' => '#^Cannot call method getLatitude\\(\\) on Geocoder\\\\Model\\\\Coordinates\\|null\\.$#',
153159
'count' => 1,
154-
'path' => __DIR__.'/src/Doctrine/ORM/GeocoderListener.php',
160+
'path' => __DIR__.'/src/Doctrine/ORM/GeocodeEntityListener.php',
155161
];
156162
$ignoreErrors[] = [
157163
// identifier: method.nonObject
158164
'message' => '#^Cannot call method getLongitude\\(\\) on Geocoder\\\\Model\\\\Coordinates\\|null\\.$#',
159165
'count' => 1,
160-
'path' => __DIR__.'/src/Doctrine/ORM/GeocoderListener.php',
166+
'path' => __DIR__.'/src/Doctrine/ORM/GeocodeEntityListener.php',
167+
];
168+
$ignoreErrors[] = [
169+
// identifier: argument.missing
170+
'message' => '#^Missing parameter \\$provider \\(string\\) in call to Bazinga\\\\GeocoderBundle\\\\Mapping\\\\ClassMetadata constructor\\.$#',
171+
'count' => 1,
172+
'path' => __DIR__.'/src/Mapping/Driver/AttributeDriver.php',
161173
];
162174
$ignoreErrors[] = [
163175
// identifier: argument.type

src/DependencyInjection/BazingaGeocoderExtension.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ public function load(array $configs, ContainerBuilder $container): void
5555
$loader->load('profiling.php');
5656
}
5757

58+
if (\array_key_exists('DoctrineBundle', $container->getParameter('kernel.bundles'))) {
59+
if (true === $config['orm']['enabled']) {
60+
$loader->load('orm.php');
61+
}
62+
} elseif (true === $config['orm']['enabled']) {
63+
throw new \LogicException('Doctrine ORM listener cannot be enabled when `doctrine/doctrine-bundle` is not installed.');
64+
}
65+
5866
if ($config['fake_ip']['enabled']) {
5967
$definition = $container->getDefinition(FakeIpPlugin::class);
6068
$definition->replaceArgument(0, $config['fake_ip']['local_ip']);

src/DependencyInjection/Configuration.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ public function getConfigTreeBuilder(): TreeBuilder
6666
->scalarNode('ip')->defaultNull()->end()
6767
->booleanNode('use_faker')->defaultFalse()->end()
6868
->end()
69+
->end()
70+
->arrayNode('orm')
71+
->addDefaultsIfNotSet()
72+
->treatFalseLike(['enabled' => false])
73+
->treatTrueLike(['enabled' => true])
74+
->treatNullLike(['enabled' => true])
75+
->children()
76+
->booleanNode('enabled')
77+
->info('Turn the Doctrine ORM listener on or off.')
78+
->defaultValue(false)
79+
->end()
80+
->end()
6981
->end();
7082

7183
return $treeBuilder;

src/Doctrine/ORM/GeocoderListener.php renamed to src/Doctrine/ORM/GeocodeEntityListener.php

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,28 @@
1414

1515
use Bazinga\GeocoderBundle\Mapping\ClassMetadata;
1616
use Bazinga\GeocoderBundle\Mapping\Driver\DriverInterface;
17-
use Doctrine\Common\EventSubscriber;
1817
use Doctrine\ORM\Event\OnFlushEventArgs;
19-
use Doctrine\ORM\Events;
2018
use Doctrine\ORM\UnitOfWork;
2119
use Geocoder\Provider\Provider;
2220
use Geocoder\Query\GeocodeQuery;
21+
use Symfony\Component\DependencyInjection\ServiceLocator;
2322

2423
/**
2524
* @author Markus Bachmann <[email protected]>
25+
* @author Pierre du Plessis <[email protected]>
2626
*/
27-
final class GeocoderListener implements EventSubscriber
27+
final class GeocodeEntityListener
2828
{
29+
/**
30+
* @param ServiceLocator<Provider> $providerLocator
31+
* @param DriverInterface $driver
32+
*/
2933
public function __construct(
30-
private readonly Provider $geocoder,
34+
private readonly ServiceLocator $providerLocator,
3135
private readonly DriverInterface $driver,
3236
) {
3337
}
3438

35-
/**
36-
* @return list<string>
37-
*/
38-
public function getSubscribedEvents(): array
39-
{
40-
return [
41-
Events::onFlush,
42-
];
43-
}
44-
4539
public function onFlush(OnFlushEventArgs $args): void
4640
{
4741
$em = $args->getObjectManager();
@@ -58,7 +52,7 @@ public function onFlush(OnFlushEventArgs $args): void
5852

5953
$uow->recomputeSingleEntityChangeSet(
6054
$em->getClassMetadata($entity::class),
61-
$entity
55+
$entity,
6256
);
6357
}
6458

@@ -77,7 +71,7 @@ public function onFlush(OnFlushEventArgs $args): void
7771

7872
$uow->recomputeSingleEntityChangeSet(
7973
$em->getClassMetadata($entity::class),
80-
$entity
74+
$entity,
8175
);
8276
}
8377
}
@@ -101,7 +95,13 @@ private function geocodeEntity(ClassMetadata $metadata, object $entity): void
10195
return;
10296
}
10397

104-
$results = $this->geocoder->geocodeQuery(GeocodeQuery::create($addressString));
98+
$serviceId = \sprintf('bazinga_geocoder.provider.%s', $metadata->provider);
99+
100+
if (!$this->providerLocator->has($serviceId)) {
101+
throw new \RuntimeException(\sprintf('The provider "%s" is invalid for object "%s".', $metadata->provider, $entity::class));
102+
}
103+
104+
$results = $this->providerLocator->get($serviceId)->geocodeQuery(GeocodeQuery::create($addressString));
105105

106106
if (!$results->isEmpty()) {
107107
$result = $results->first();

src/Mapping/Attributes/Geocodeable.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,11 @@
1818
#[\Attribute(\Attribute::TARGET_CLASS)]
1919
class Geocodeable
2020
{
21+
/**
22+
* @param non-empty-string $provider
23+
*/
24+
public function __construct(
25+
public readonly string $provider,
26+
) {
27+
}
2128
}

src/Mapping/ClassMetadata.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
*/
1818
final class ClassMetadata
1919
{
20+
/**
21+
* @param non-empty-string $provider
22+
*/
2023
public function __construct(
24+
public readonly string $provider,
2125
public readonly ?\ReflectionProperty $addressProperty = null,
2226
public readonly ?\ReflectionProperty $latitudeProperty = null,
2327
public readonly ?\ReflectionProperty $longitudeProperty = null,

src/Mapping/Driver/AttributeDriver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function loadMetadataFromObject(object $object): ClassMetadata
4343
throw new MappingException(sprintf('The class "%s" is not geocodeable', $object::class));
4444
}
4545

46-
$args = [];
46+
$args = ['provider' => $attributes[0]->newInstance()->provider];
4747

4848
foreach ($reflection->getProperties() as $property) {
4949
foreach ($property->getAttributes() as $attribute) {

0 commit comments

Comments
 (0)