<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\ResourceBundle\EventListener;
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Webmozart\Assert\Assert;
final class ORMMappedSuperClassSubscriber extends AbstractDoctrineSubscriber
{
public function getSubscribedEvents(): array
{
return [
Events::loadClassMetadata,
];
}
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void
{
$metadata = $eventArgs->getClassMetadata();
if (!$metadata->isMappedSuperclass) {
$this->setAssociationMappings($metadata, $eventArgs->getEntityManager()->getConfiguration());
} else {
$this->unsetAssociationMappings($metadata);
}
}
private function setAssociationMappings(ClassMetadataInfo $metadata, Configuration $configuration): void
{
$class = $metadata->getName();
if (!class_exists($class)) {
return;
}
$metadataDriver = $configuration->getMetadataDriverImpl();
Assert::isInstanceOf($metadataDriver, MappingDriver::class);
foreach (class_parents($class) as $parent) {
if (false === in_array($parent, $metadataDriver->getAllClassNames(), true)) {
continue;
}
$parentMetadata = new ClassMetadata(
$parent,
$configuration->getNamingStrategy()
);
// Wakeup Reflection
$parentMetadata->wakeupReflection($this->getReflectionService());
// Load Metadata
$metadataDriver->loadMetadataForClass($parent, $parentMetadata);
/** @psalm-suppress InvalidArgument */
if (false === $this->isResource($parentMetadata)) {
continue;
}
if ($parentMetadata->isMappedSuperclass) {
foreach ($parentMetadata->getAssociationMappings() as $key => $value) {
if ($this->isRelation($value['type']) && !isset($metadata->associationMappings[$key])) {
$metadata->associationMappings[$key] = $value;
}
}
}
}
}
private function unsetAssociationMappings(ClassMetadataInfo $metadata): void
{
/** @psalm-suppress InvalidArgument */
if (false === $this->isResource($metadata)) {
return;
}
foreach ($metadata->getAssociationMappings() as $key => $value) {
if ($this->isRelation($value['type'])) {
unset($metadata->associationMappings[$key]);
}
}
}
private function isRelation(int $type): bool
{
return in_array(
$type,
[
ClassMetadataInfo::MANY_TO_MANY,
ClassMetadataInfo::ONE_TO_MANY,
ClassMetadataInfo::ONE_TO_ONE,
],
true
);
}
}