Skip to content

Commit f85c543

Browse files
committed
Auto register doctrine services, and support multiple metadata drivers
1 parent dafe3a2 commit f85c543

File tree

11 files changed

+259
-152
lines changed

11 files changed

+259
-152
lines changed

DependencyInjection/BazingaGeocoderExtension.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ public function load(array $configs, ContainerBuilder $container)
5353
$loader->load('profiling.yml');
5454
}
5555

56+
if (array_key_exists('DoctrineBundle', $container->getParameter('kernel.bundles'))) {
57+
$loader->load('doctrine.yml');
58+
}
59+
5660
if ($config['fake_ip']['enabled']) {
5761
$definition = $container->getDefinition(FakeIpPlugin::class);
5862
$definition->replaceArgument(0, $config['fake_ip']['local_ip']);
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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\Doctrine\ORM;
14+
15+
use Bazinga\GeocoderBundle\Mapping\ClassMetadata;
16+
use Bazinga\GeocoderBundle\Mapping\Driver\DriverInterface;
17+
use Doctrine\Common\EventSubscriber;
18+
use Doctrine\ORM\Event\OnFlushEventArgs;
19+
use Doctrine\ORM\Events;
20+
use Doctrine\ORM\UnitOfWork;
21+
use Geocoder\Query\GeocodeQuery;
22+
use Symfony\Component\DependencyInjection\ServiceLocator;
23+
24+
/**
25+
* @author Markus Bachmann <markus.bachmann@bachi.biz>
26+
* @author Pierre du Plessis <pdples@gmail.com>
27+
*/
28+
class GeocodeEntityListener implements EventSubscriber
29+
{
30+
/**
31+
* @var DriverInterface
32+
*/
33+
private $driver;
34+
35+
/**
36+
* @var ServiceLocator
37+
*/
38+
private $providerLocator;
39+
40+
public function __construct(ServiceLocator $providerLocator, DriverInterface $driver)
41+
{
42+
$this->driver = $driver;
43+
$this->providerLocator = $providerLocator;
44+
}
45+
46+
/**
47+
* {@inheritdoc}
48+
*/
49+
public function getSubscribedEvents()
50+
{
51+
return [
52+
Events::onFlush,
53+
];
54+
}
55+
56+
public function onFlush(OnFlushEventArgs $args)
57+
{
58+
$em = $args->getEntityManager();
59+
$uow = $em->getUnitOfWork();
60+
61+
foreach ($uow->getScheduledEntityInsertions() as $entity) {
62+
if (!$this->driver->isGeocodeable($entity)) {
63+
continue;
64+
}
65+
66+
/** @var ClassMetadata $metadata */
67+
$metadata = $this->driver->loadMetadataFromObject($entity);
68+
69+
$this->geocodeEntity($metadata, $entity);
70+
71+
$uow->recomputeSingleEntityChangeSet(
72+
$em->getClassMetadata(get_class($entity)),
73+
$entity
74+
);
75+
}
76+
77+
foreach ($uow->getScheduledEntityUpdates() as $entity) {
78+
if (!$this->driver->isGeocodeable($entity)) {
79+
continue;
80+
}
81+
82+
/** @var ClassMetadata $metadata */
83+
$metadata = $this->driver->loadMetadataFromObject($entity);
84+
85+
if (!$this->shouldGeocode($metadata, $uow, $entity)) {
86+
continue;
87+
}
88+
89+
$this->geocodeEntity($metadata, $entity);
90+
91+
$uow->recomputeSingleEntityChangeSet(
92+
$em->getClassMetadata(get_class($entity)),
93+
$entity
94+
);
95+
}
96+
}
97+
98+
/**
99+
* @param object $entity
100+
*/
101+
private function geocodeEntity(ClassMetadata $metadata, $entity)
102+
{
103+
if (null !== $metadata->addressGetter) {
104+
$address = $metadata->addressGetter->invoke($entity);
105+
} else {
106+
$address = $metadata->addressProperty->getValue($entity);
107+
}
108+
109+
if (empty($address)) {
110+
return;
111+
}
112+
113+
$serviceId = sprintf('bazinga_geocoder.provider.%s', $metadata->provider);
114+
115+
if (!$this->providerLocator->has($serviceId)) {
116+
throw new \RuntimeException(sprintf('The provider "%s" is invalid for object "%s".', $metadata->provider, get_class($entity)));
117+
}
118+
119+
$results = $this->providerLocator->get($serviceId)->geocodeQuery(GeocodeQuery::create($address));
120+
121+
if (!$results->isEmpty()) {
122+
$result = $results->first();
123+
$metadata->latitudeProperty->setValue($entity, $result->getCoordinates()->getLatitude());
124+
$metadata->longitudeProperty->setValue($entity, $result->getCoordinates()->getLongitude());
125+
}
126+
}
127+
128+
/**
129+
* @param object $entity
130+
*/
131+
private function shouldGeocode(ClassMetadata $metadata, UnitOfWork $unitOfWork, $entity): bool
132+
{
133+
if (null !== $metadata->addressGetter) {
134+
return true;
135+
}
136+
137+
$changeSet = $unitOfWork->getEntityChangeSet($entity);
138+
139+
return isset($changeSet[$metadata->addressProperty->getName()]);
140+
}
141+
}

Doctrine/ORM/GeocoderListener.php

Lines changed: 9 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -12,123 +12,26 @@
1212

1313
namespace Bazinga\GeocoderBundle\Doctrine\ORM;
1414

15-
use Bazinga\GeocoderBundle\Mapping\ClassMetadata;
1615
use Bazinga\GeocoderBundle\Mapping\Driver\DriverInterface;
17-
use Doctrine\Common\EventSubscriber;
18-
use Doctrine\ORM\Event\OnFlushEventArgs;
19-
use Doctrine\ORM\Events;
20-
use Doctrine\ORM\UnitOfWork;
2116
use Geocoder\Provider\Provider;
22-
use Geocoder\Query\GeocodeQuery;
17+
use Symfony\Component\DependencyInjection\ServiceLocator;
2318

2419
/**
2520
* @author Markus Bachmann <markus.bachmann@bachi.biz>
2621
*/
27-
class GeocoderListener implements EventSubscriber
22+
class GeocoderListener extends GeocodeEntityListener
2823
{
29-
/**
30-
* @var DriverInterface
31-
*/
32-
private $driver;
33-
34-
/**
35-
* @var Provider
36-
*/
37-
private $geocoder;
38-
3924
public function __construct(Provider $geocoder, DriverInterface $driver)
4025
{
41-
$this->driver = $driver;
42-
$this->geocoder = $geocoder;
43-
}
44-
45-
/**
46-
* {@inheritdoc}
47-
*/
48-
public function getSubscribedEvents()
49-
{
50-
return [
51-
Events::onFlush,
52-
];
53-
}
54-
55-
public function onFlush(OnFlushEventArgs $args)
56-
{
57-
$em = $args->getEntityManager();
58-
$uow = $em->getUnitOfWork();
59-
60-
foreach ($uow->getScheduledEntityInsertions() as $entity) {
61-
if (!$this->driver->isGeocodeable($entity)) {
62-
continue;
63-
}
64-
65-
/** @var ClassMetadata $metadata */
66-
$metadata = $this->driver->loadMetadataFromObject($entity);
67-
68-
$this->geocodeEntity($metadata, $entity);
69-
70-
$uow->recomputeSingleEntityChangeSet(
71-
$em->getClassMetadata(get_class($entity)),
72-
$entity
73-
);
74-
}
75-
76-
foreach ($uow->getScheduledEntityUpdates() as $entity) {
77-
if (!$this->driver->isGeocodeable($entity)) {
78-
continue;
79-
}
80-
81-
/** @var ClassMetadata $metadata */
82-
$metadata = $this->driver->loadMetadataFromObject($entity);
26+
@trigger_error(sprintf('The class "%s" is deprecated and will be removed from a future version. Please remove it from your service definition.', self::class));
8327

84-
if (!$this->shouldGeocode($metadata, $uow, $entity)) {
85-
continue;
28+
$locator = new ServiceLocator(array(
29+
'bazinga_geocoder.provider.' =>
30+
function () use ($geocoder) {
31+
return $geocoder;
8632
}
33+
));
8734

88-
$this->geocodeEntity($metadata, $entity);
89-
90-
$uow->recomputeSingleEntityChangeSet(
91-
$em->getClassMetadata(get_class($entity)),
92-
$entity
93-
);
94-
}
95-
}
96-
97-
/**
98-
* @param object $entity
99-
*/
100-
private function geocodeEntity(ClassMetadata $metadata, $entity)
101-
{
102-
if (null !== $metadata->addressGetter) {
103-
$address = $metadata->addressGetter->invoke($entity);
104-
} else {
105-
$address = $metadata->addressProperty->getValue($entity);
106-
}
107-
108-
if (empty($address)) {
109-
return;
110-
}
111-
112-
$results = $this->geocoder->geocodeQuery(GeocodeQuery::create($address));
113-
114-
if (!$results->isEmpty()) {
115-
$result = $results->first();
116-
$metadata->latitudeProperty->setValue($entity, $result->getCoordinates()->getLatitude());
117-
$metadata->longitudeProperty->setValue($entity, $result->getCoordinates()->getLongitude());
118-
}
119-
}
120-
121-
/**
122-
* @param object $entity
123-
*/
124-
private function shouldGeocode(ClassMetadata $metadata, UnitOfWork $unitOfWork, $entity): bool
125-
{
126-
if (null !== $metadata->addressGetter) {
127-
return true;
128-
}
129-
130-
$changeSet = $unitOfWork->getEntityChangeSet($entity);
131-
132-
return isset($changeSet[$metadata->addressProperty->getName()]);
35+
parent::__construct($locator, $driver);
13336
}
13437
}

Mapping/ClassMetadata.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,9 @@ class ClassMetadata
3636
* @var \ReflectionMethod
3737
*/
3838
public $addressGetter;
39+
40+
/**
41+
* @var string|null
42+
*/
43+
public $provider = null;
3944
}

Mapping/Driver/AnnotationDriver.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public function loadMetadataFromObject($object)
4444
}
4545

4646
$metadata = new ClassMetadata();
47+
$metadata->provider = $annotation->provider;
4748

4849
foreach ($reflection->getProperties() as $property) {
4950
foreach ($this->reader->getPropertyAnnotations($property) as $annotation) {

Mapping/Driver/AttributeDriver.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public function loadMetadataFromObject($object): ClassMetadata
5959
}
6060

6161
$metadata = new ClassMetadata();
62+
$metadata->provider = $attributes[0]->newInstance()->provider;
6263

6364
foreach ($reflection->getProperties() as $property) {
6465
foreach ($property->getAttributes() as $attribute) {

Mapping/Driver/ChainDriver.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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\Mapping\Driver;
14+
15+
use Bazinga\GeocoderBundle\Mapping\Annotations;
16+
use Bazinga\GeocoderBundle\Mapping\ClassMetadata;
17+
use Bazinga\GeocoderBundle\Mapping\Exception;
18+
use Bazinga\GeocoderBundle\Mapping\Exception\MappingException;
19+
use Doctrine\Common\Annotations\Reader;
20+
21+
/**
22+
* @author Pierre du Plessis <pdples@gmail.com>
23+
*/
24+
class ChainDriver implements DriverInterface
25+
{
26+
private $drivers;
27+
28+
public function __construct(iterable $drivers)
29+
{
30+
$this->drivers = $drivers;
31+
}
32+
33+
public function isGeocodeable($object): bool
34+
{
35+
foreach ($this->drivers as $driver) {
36+
if ($driver->isGeocodeable($object)) {
37+
return true;
38+
}
39+
}
40+
41+
return false;
42+
}
43+
44+
public function loadMetadataFromObject($object)
45+
{
46+
foreach ($this->drivers as $driver) {
47+
try {
48+
return $driver->loadMetadataFromObject($object);
49+
} catch (MappingException $exception) {
50+
continue;
51+
}
52+
}
53+
54+
throw new MappingException(sprintf('The class %s is not geocodeable', get_class($object)));
55+
}
56+
}

Mapping/Driver/DriverInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@
1212

1313
namespace Bazinga\GeocoderBundle\Mapping\Driver;
1414

15+
use Bazinga\GeocoderBundle\Mapping\Exception\MappingException;
16+
1517
interface DriverInterface
1618
{
1719
public function isGeocodeable($object): bool;
1820

21+
/**
22+
* @throws MappingException
23+
*/
1924
public function loadMetadataFromObject($object);
2025
}

Resources/config/doctrine.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
services:
2+
Bazinga\GeocoderBundle\Mapping\Driver\AnnotationDriver:
3+
class: Bazinga\GeocoderBundle\Mapping\Driver\AnnotationDriver
4+
arguments:
5+
- '@annotations.reader'
6+
tags:
7+
- { name: bazinga_geocoder.metadata.driver }
8+
9+
Bazinga\GeocoderBundle\Doctrine\ORM\GeocoderListener:
10+
class: Bazinga\GeocoderBundle\Doctrine\ORM\GeocoderListener
11+
arguments:
12+
- !tagged_locator 'bazinga_geocoder.provider'
13+
- '@Bazinga\GeocoderBundle\Mapping\Driver\DriverInterface'
14+
tags:
15+
- doctrine.event_subscriber

0 commit comments

Comments
 (0)