Created
February 21, 2019 12:42
-
-
Save axelvnk/492eb3c698ecc298f92b3e4eec811bea to your computer and use it in GitHub Desktop.
Make classes from your bundles overridable
This file contains hidden or 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 Axelvnk\ResourceBundle\EventListener; | |
use Doctrine\Common\EventSubscriber; | |
use Doctrine\Common\Persistence\Mapping\RuntimeReflectionService; | |
use Doctrine\ORM\Configuration; | |
use Doctrine\ORM\Event\LoadClassMetadataEventArgs; | |
use Doctrine\ORM\Events; | |
use Doctrine\ORM\Mapping\ClassMetadata; | |
use Doctrine\ORM\Mapping\ClassMetadataInfo; | |
use Axelvnk\ResourceBundle\Service\Registry; | |
class MappedSuperClassSubscriber implements EventSubscriber | |
{ | |
/** | |
* @var RuntimeReflectionService | |
*/ | |
private $reflectionService; | |
/** | |
* @var string[]|array | |
*/ | |
private $allEntities; | |
/** | |
* @var Registry | |
*/ | |
private $registry; | |
public function __construct(Registry $registry) | |
{ | |
$this->allEntities = []; | |
$this->registry = $registry; | |
} | |
/** | |
* @return array | |
*/ | |
public function getSubscribedEvents() | |
{ | |
return [ | |
Events::loadClassMetadata, | |
]; | |
} | |
/** | |
* @param LoadClassMetadataEventArgs $eventArgs | |
*/ | |
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs) | |
{ | |
$configuration = $eventArgs->getEntityManager()->getConfiguration(); | |
$this->loadEntitiesArray($configuration); | |
$metadata = $eventArgs->getClassMetadata(); | |
$this->convertToEntityIfNeeded($metadata); | |
if ($metadata->isMappedSuperclass) { | |
$this->unsetAssociationMappings($metadata); | |
return; | |
} | |
$this->setAssociationMappings($metadata, $configuration); | |
} | |
/** | |
* @param ClassMetadataInfo $metadata | |
*/ | |
private function convertToEntityIfNeeded(ClassMetadataInfo $metadata) | |
{ | |
if (false === $metadata->isMappedSuperclass) { | |
return; | |
} | |
if ($this->registry->containsResource($metadata->getName()) === false) { | |
return; | |
} | |
if ($this->hasSubclasses($metadata->getName())) { | |
return; | |
} | |
$metadata->isMappedSuperclass = false; | |
} | |
/** | |
* @param ClassMetadataInfo $metadata | |
* @param Configuration $configuration | |
*/ | |
private function setAssociationMappings(ClassMetadataInfo $metadata, $configuration) | |
{ | |
$classParents = class_parents($metadata->getName()); | |
foreach ($classParents as $parent) { | |
if (false === $this->isEntity($parent)) { | |
continue; | |
} | |
$parentMetadata = new ClassMetadata($parent, $configuration->getNamingStrategy()); | |
$parentMetadata->wakeupReflection($this->getReflectionService()); | |
$configuration->getMetadataDriverImpl()->loadMetadataForClass($parent, $parentMetadata); | |
if (false === $parentMetadata->isMappedSuperclass) { | |
continue; | |
} | |
foreach ($parentMetadata->getAssociationMappings() as $key => $value) { | |
if ($this->hasRelation($value['type'])) { | |
$metadata->associationMappings[$key] = $value; | |
} | |
} | |
} | |
} | |
/** | |
* @param ClassMetadataInfo $metadata | |
*/ | |
private function unsetAssociationMappings(ClassMetadataInfo $metadata) | |
{ | |
foreach ($metadata->getAssociationMappings() as $key => $value) { | |
if ($this->hasRelation($value['type'])) { | |
unset($metadata->associationMappings[$key]); | |
} | |
} | |
} | |
/** | |
* @param int $type | |
* | |
* @return bool | |
*/ | |
private function hasRelation($type) | |
{ | |
return in_array( | |
$type, | |
[ | |
ClassMetadataInfo::MANY_TO_MANY, | |
ClassMetadataInfo::ONE_TO_MANY, | |
ClassMetadataInfo::ONE_TO_ONE, | |
], | |
true | |
); | |
} | |
protected function getReflectionService() | |
{ | |
if ($this->reflectionService === null) { | |
$this->reflectionService = new RuntimeReflectionService(); | |
} | |
return $this->reflectionService; | |
} | |
/** | |
* @param string $mappedSuperClass | |
* | |
* @return bool | |
*/ | |
private function hasSubclasses($mappedSuperClass): bool | |
{ | |
foreach ($this->allEntities as $entityClass) { | |
if (is_subclass_of($entityClass, $mappedSuperClass)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* @param string $class | |
* | |
* @return bool | |
*/ | |
private function isEntity($class): bool | |
{ | |
return in_array($class, $this->allEntities); | |
} | |
/** | |
* @param Configuration $configuration | |
*/ | |
private function loadEntitiesArray(Configuration $configuration) | |
{ | |
if (count($this->allEntities) === 0) { | |
$this->allEntities = $configuration->getMetadataDriverImpl()->getAllClassNames(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment