Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boekkooi/bbcba15d321e2a3f4a3c to your computer and use it in GitHub Desktop.
Save boekkooi/bbcba15d321e2a3f4a3c to your computer and use it in GitHub Desktop.
Doctrine EntityReference
<?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;
}
}
<?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);
}
}
<?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;
}
}
<?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