Created
February 4, 2015 14:40
-
-
Save boekkooi/bbcba15d321e2a3f4a3c to your computer and use it in GitHub Desktop.
Doctrine EntityReference
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Boekkooi\Bundle\FrameworkBundle\EventListener; | |
use Doctrine\Common\EventSubscriber; | |
use Doctrine\ORM\Event\LoadClassMetadataEventArgs; | |
use Doctrine\ORM\Mapping as ORM; | |
use Doctrine\ORM\Events; | |
use Doctrine\ORM\Mapping\ClassMetadataInfo; | |
use Doctrine\ORM\Mapping\MappingException; | |
use Doctrine\ORM\Tools\AttachEntityListenersListener; | |
use Boekkooi\Bundle\FrameworkBundle\Model\EntityReference; | |
/** | |
* @author Warnar Boekkooi <warnar@boekkooi.net> | |
*/ | |
class EntityReferenceEventSubscriber implements EventSubscriber | |
{ | |
/** | |
* Returns an array of events this subscriber wants to listen to. | |
* | |
* @return array | |
*/ | |
public function getSubscribedEvents() | |
{ | |
return [ | |
Events::loadClassMetadata | |
]; | |
} | |
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) | |
{ | |
/** @var \Doctrine\ORM\Mapping\ClassMetadataInfo $classMetadata */ | |
$classMetadata = $eventArgs->getClassMetadata(); | |
foreach ($classMetadata->embeddedClasses as $fieldName => $fieldInfo) { | |
if ($fieldInfo['class'] !== EntityReference::class) { | |
continue; | |
} | |
// Autoload the reference | |
$this->addEntityListener($classMetadata, Events::postLoad, Hooks\PostLoadHandler::class, $fieldName); | |
// Persist reference | |
$this->addEntityListener($classMetadata, Events::postPersist, Hooks\PersistHandler::class, $fieldName); | |
} | |
} | |
private function addEntityListener(ClassMetadataInfo $metadata, $eventName, $class, $method) | |
{ | |
$class = $metadata->fullyQualifiedClassName($class); | |
$listener = array( | |
'class' => $class, | |
'method' => $method | |
); | |
if ( ! class_exists($class)) { | |
throw MappingException::entityListenerClassNotFound($class, $metadata->name); | |
} | |
if (isset($metadata->entityListeners[$eventName]) && in_array($listener, $metadata->entityListeners[$eventName])) { | |
throw MappingException::duplicateEntityListener($class, $method, $metadata->name); | |
} | |
$metadata->entityListeners[$eventName][] = $listener; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Boekkooi\Bundle\FrameworkBundle\EventListener\Hooks; | |
use Boekkooi\Bundle\FrameworkBundle\Model\EntityReference; | |
/** | |
* @author Warnar Boekkooi <warnar@boekkooi.net> | |
*/ | |
class PersistHandler | |
{ | |
private $visited; | |
/** | |
* @param $name | |
* @param $arguments | |
*/ | |
public function __call($name, $arguments) | |
{ | |
$entity = $arguments[0]; | |
/** @var \Doctrine\ORM\Event\LifecycleEventArgs $event */ | |
$event = $arguments[1]; | |
$entityReference = $this->getReference($name, $entity); | |
if ($entityReference === null) { | |
return; | |
} | |
$reference = $entityReference->getReference(); | |
if ($reference === null) { | |
return; | |
} | |
$em = $event->getEntityManager(); | |
$uow = $em->getUnitOfWork(); | |
if ($uow->isEntityScheduled($reference) || $uow->isInIdentityMap($reference)) { | |
return; | |
} | |
$em->persist($reference); | |
$em->flush($reference); | |
} | |
/** | |
* @param string $propertyName | |
* @param object $entity | |
* @return EntityReference|null | |
*/ | |
private function getReference($propertyName, $entity) | |
{ | |
$referenceProperty = new \ReflectionProperty(get_class($entity), $propertyName); | |
$referenceProperty->setAccessible(true); | |
return $referenceProperty->getValue($entity); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Boekkooi\Bundle\FrameworkBundle\EventListener\Hooks; | |
use Boekkooi\Bundle\FrameworkBundle\Model\EntityReference; | |
/** | |
* @author Warnar Boekkooi <warnar@boekkooi.net> | |
*/ | |
class PostLoadHandler | |
{ | |
/** | |
* @var \ReflectionProperty|null | |
*/ | |
private static $property; | |
/** | |
* Since the method name is the property/field name | |
* | |
* @param $name | |
* @param $arguments | |
*/ | |
public function __call($name, $arguments) | |
{ | |
$entity = $arguments[0]; | |
/** @var \Doctrine\ORM\Event\LifecycleEventArgs $event */ | |
$event = $arguments[1]; | |
$entityReference = $this->getReference($name, $entity); | |
if ($entityReference === null || $entityReference->getId() === null || $entityReference->getClass() === null) { | |
return; | |
} | |
// Set the reference | |
$this->getPropertyReflection()->setValue( | |
$entityReference, | |
$event->getEntityManager()->getReference($entityReference->getClass(), $entityReference->getId()) | |
); | |
} | |
/** | |
* @param string $propertyName | |
* @param object $entity | |
* @return EntityReference|null | |
*/ | |
private function getReference($propertyName, $entity) | |
{ | |
$referenceProperty = new \ReflectionProperty(get_class($entity), $propertyName); | |
$referenceProperty->setAccessible(true); | |
return $referenceProperty->getValue($entity); | |
} | |
private function getPropertyReflection() | |
{ | |
if (self::$property === null) { | |
self::$property = new \ReflectionProperty(EntityReference::class, 'reference'); | |
self::$property->setAccessible(true); | |
} | |
return self::$property; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Boekkooi\Bundle\FrameworkBundle\Model; | |
use Boekkooi\Bundle\DoctrineEventStoreBundle\Model\EventSource; | |
use Doctrine\Common\Util\ClassUtils; | |
use Doctrine\ORM\Mapping as ORM; | |
/** | |
* @author Warnar Boekkooi <warnar@boekkooi.net> | |
* | |
* @ORM\Embeddable | |
*/ | |
final class EntityReference | |
{ | |
/** | |
* @ORM\Column(name="id", type="uuid") | |
*/ | |
private $id; | |
/** | |
* @ORM\Column(name="class", type = "string") | |
*/ | |
private $class; | |
private $reference; | |
public function __construct($entity) | |
{ | |
if (!$entity instanceof EventSource) { | |
throw new \LogicException('Only EventSource models/entities maybe referenced!'); | |
} | |
$this->id = $entity->getId(); | |
$this->class = ClassUtils::getClass($entity); | |
$this->reference = $entity; | |
} | |
public function getReference() | |
{ | |
return $this->reference; | |
} | |
public function getId() | |
{ | |
return $this->class; | |
} | |
public function getClass() | |
{ | |
return $this->class; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment