Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto register doctrine services #303

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions DependencyInjection/BazingaGeocoderExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ public function load(array $configs, ContainerBuilder $container)
$loader->load('profiling.yml');
}

if (array_key_exists('DoctrineBundle', $container->getParameter('kernel.bundles'))) {
$loader->load('doctrine.yml');
}

if ($config['fake_ip']['enabled']) {
$definition = $container->getDefinition(FakeIpPlugin::class);
$definition->replaceArgument(0, $config['fake_ip']['local_ip']);
Expand Down
141 changes: 141 additions & 0 deletions Doctrine/ORM/GeocodeEntityListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

declare(strict_types=1);

/*
* This file is part of the BazingaGeocoderBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/

namespace Bazinga\GeocoderBundle\Doctrine\ORM;

use Bazinga\GeocoderBundle\Mapping\ClassMetadata;
use Bazinga\GeocoderBundle\Mapping\Driver\DriverInterface;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\UnitOfWork;
use Geocoder\Query\GeocodeQuery;
use Symfony\Component\DependencyInjection\ServiceLocator;

/**
* @author Markus Bachmann <[email protected]>
* @author Pierre du Plessis <[email protected]>
*/
class GeocodeEntityListener implements EventSubscriber
{
/**
* @var DriverInterface
*/
private $driver;

/**
* @var ServiceLocator
*/
private $providerLocator;

public function __construct(ServiceLocator $providerLocator, DriverInterface $driver)
{
$this->driver = $driver;
$this->providerLocator = $providerLocator;
}

/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
return [
Events::onFlush,
];
}

public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();

foreach ($uow->getScheduledEntityInsertions() as $entity) {
if (!$this->driver->isGeocodeable($entity)) {
continue;
}

/** @var ClassMetadata $metadata */
$metadata = $this->driver->loadMetadataFromObject($entity);

$this->geocodeEntity($metadata, $entity);

$uow->recomputeSingleEntityChangeSet(
$em->getClassMetadata(get_class($entity)),
$entity
);
}

foreach ($uow->getScheduledEntityUpdates() as $entity) {
if (!$this->driver->isGeocodeable($entity)) {
continue;
}

/** @var ClassMetadata $metadata */
$metadata = $this->driver->loadMetadataFromObject($entity);

if (!$this->shouldGeocode($metadata, $uow, $entity)) {
continue;
}

$this->geocodeEntity($metadata, $entity);

$uow->recomputeSingleEntityChangeSet(
$em->getClassMetadata(get_class($entity)),
$entity
);
}
}

/**
* @param object $entity
*/
private function geocodeEntity(ClassMetadata $metadata, $entity)
{
if (null !== $metadata->addressGetter) {
$address = $metadata->addressGetter->invoke($entity);
} else {
$address = $metadata->addressProperty->getValue($entity);
}

if (empty($address)) {
return;
}

$serviceId = sprintf('bazinga_geocoder.provider.%s', $metadata->provider);

if (!$this->providerLocator->has($serviceId)) {
throw new \RuntimeException(sprintf('The provider "%s" is invalid for object "%s".', $metadata->provider, get_class($entity)));
}

$results = $this->providerLocator->get($serviceId)->geocodeQuery(GeocodeQuery::create($address));

if (!$results->isEmpty()) {
$result = $results->first();
$metadata->latitudeProperty->setValue($entity, $result->getCoordinates()->getLatitude());
$metadata->longitudeProperty->setValue($entity, $result->getCoordinates()->getLongitude());
}
}

/**
* @param object $entity
*/
private function shouldGeocode(ClassMetadata $metadata, UnitOfWork $unitOfWork, $entity): bool
{
if (null !== $metadata->addressGetter) {
return true;
}

$changeSet = $unitOfWork->getEntityChangeSet($entity);

return isset($changeSet[$metadata->addressProperty->getName()]);
}
}
116 changes: 9 additions & 107 deletions Doctrine/ORM/GeocoderListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,123 +12,25 @@

namespace Bazinga\GeocoderBundle\Doctrine\ORM;

use Bazinga\GeocoderBundle\Mapping\ClassMetadata;
use Bazinga\GeocoderBundle\Mapping\Driver\DriverInterface;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\UnitOfWork;
use Geocoder\Provider\Provider;
use Geocoder\Query\GeocodeQuery;
use Symfony\Component\DependencyInjection\ServiceLocator;

/**
* @author Markus Bachmann <[email protected]>
*/
class GeocoderListener implements EventSubscriber
class GeocoderListener extends GeocodeEntityListener
{
/**
* @var DriverInterface
*/
private $driver;

/**
* @var Provider
*/
private $geocoder;

public function __construct(Provider $geocoder, DriverInterface $driver)
{
$this->driver = $driver;
$this->geocoder = $geocoder;
}

/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
return [
Events::onFlush,
];
}

public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();

foreach ($uow->getScheduledEntityInsertions() as $entity) {
if (!$this->driver->isGeocodeable($entity)) {
continue;
}

/** @var ClassMetadata $metadata */
$metadata = $this->driver->loadMetadataFromObject($entity);

$this->geocodeEntity($metadata, $entity);

$uow->recomputeSingleEntityChangeSet(
$em->getClassMetadata(get_class($entity)),
$entity
);
}

foreach ($uow->getScheduledEntityUpdates() as $entity) {
if (!$this->driver->isGeocodeable($entity)) {
continue;
}

/** @var ClassMetadata $metadata */
$metadata = $this->driver->loadMetadataFromObject($entity);

if (!$this->shouldGeocode($metadata, $uow, $entity)) {
continue;
}

$this->geocodeEntity($metadata, $entity);

$uow->recomputeSingleEntityChangeSet(
$em->getClassMetadata(get_class($entity)),
$entity
);
}
}

/**
* @param object $entity
*/
private function geocodeEntity(ClassMetadata $metadata, $entity)
{
if (null !== $metadata->addressGetter) {
$address = $metadata->addressGetter->invoke($entity);
} else {
$address = $metadata->addressProperty->getValue($entity);
}

if (empty($address)) {
return;
}

$results = $this->geocoder->geocodeQuery(GeocodeQuery::create($address));

if (!$results->isEmpty()) {
$result = $results->first();
$metadata->latitudeProperty->setValue($entity, $result->getCoordinates()->getLatitude());
$metadata->longitudeProperty->setValue($entity, $result->getCoordinates()->getLongitude());
}
}

/**
* @param object $entity
*/
private function shouldGeocode(ClassMetadata $metadata, UnitOfWork $unitOfWork, $entity): bool
{
if (null !== $metadata->addressGetter) {
return true;
}
@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));
Copy link
Member

@norkunas norkunas Feb 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@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));
@trigger_error(sprintf('The class "%s" is deprecated and will be removed in 6.0 version. Please use "%s".', self::class, GeocodeEntityListener::class));


$changeSet = $unitOfWork->getEntityChangeSet($entity);
$locator = new ServiceLocator([
'bazinga_geocoder.provider.' => function () use ($geocoder) {
return $geocoder;
},
]);

return isset($changeSet[$metadata->addressProperty->getName()]);
parent::__construct($locator, $driver);
}
}
9 changes: 9 additions & 0 deletions Mapping/Annotations/Geocodeable.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,13 @@
*/
class Geocodeable
{
/**
* @var string
*/
public $provider = null;

public function __construct(array $options = [], string $provider = null)
{
$this->provider = $options['provider'] ?? $provider;
}
}
5 changes: 5 additions & 0 deletions Mapping/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,9 @@ class ClassMetadata
* @var \ReflectionMethod
*/
public $addressGetter;

/**
* @var string|null
*/
public $provider = null;
}
1 change: 1 addition & 0 deletions Mapping/Driver/AnnotationDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public function loadMetadataFromObject($object)
}

$metadata = new ClassMetadata();
$metadata->provider = $annotation->provider;

foreach ($reflection->getProperties() as $property) {
foreach ($this->reader->getPropertyAnnotations($property) as $annotation) {
Expand Down
1 change: 1 addition & 0 deletions Mapping/Driver/AttributeDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public function loadMetadataFromObject($object): ClassMetadata
}

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

foreach ($reflection->getProperties() as $property) {
foreach ($property->getAttributes() as $attribute) {
Expand Down
52 changes: 52 additions & 0 deletions Mapping/Driver/ChainDriver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

/*
* This file is part of the BazingaGeocoderBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/

namespace Bazinga\GeocoderBundle\Mapping\Driver;

use Bazinga\GeocoderBundle\Mapping\Exception\MappingException;

/**
* @author Pierre du Plessis <[email protected]>
*/
class ChainDriver implements DriverInterface
{
private $drivers;

public function __construct(iterable $drivers)
{
$this->drivers = $drivers;
}

public function isGeocodeable($object): bool
{
foreach ($this->drivers as $driver) {
if ($driver->isGeocodeable($object)) {
return true;
}
}

return false;
}

public function loadMetadataFromObject($object)
{
foreach ($this->drivers as $driver) {
try {
return $driver->loadMetadataFromObject($object);
} catch (MappingException $exception) {
continue;
}
}

throw new MappingException(sprintf('The class %s is not geocodeable', get_class($object)));
}
}
5 changes: 5 additions & 0 deletions Mapping/Driver/DriverInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@

namespace Bazinga\GeocoderBundle\Mapping\Driver;

use Bazinga\GeocoderBundle\Mapping\Exception\MappingException;

interface DriverInterface
{
public function isGeocodeable($object): bool;

/**
* @throws MappingException
*/
public function loadMetadataFromObject($object);
}
Loading