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

Compatibility with ORM 3 #1722

Merged
merged 1 commit into from
Nov 12, 2023
Merged
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: 2 additions & 2 deletions DataCollector/DoctrineDataCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Tools\SchemaValidator;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
Expand Down Expand Up @@ -105,7 +105,7 @@ public function collect(Request $request, Response $response, ?Throwable $except
assert($factory instanceof AbstractClassMetadataFactory);

foreach ($factory->getLoadedMetadata() as $class) {
assert($class instanceof ClassMetadataInfo);
assert($class instanceof ClassMetadata);
if (isset($entities[$name][$class->getName()])) {
continue;
}
Expand Down
9 changes: 5 additions & 4 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;

use Doctrine\Common\Proxy\AbstractProxyFactory;
use Doctrine\DBAL\Schema\LegacySchemaManagerFactory;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Proxy\ProxyFactory;
use InvalidArgumentException;
use ReflectionClass;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
Expand All @@ -32,6 +32,7 @@
use function is_int;
use function is_string;
use function key;
use function method_exists;
use function reset;
use function sprintf;
use function strlen;
Expand Down Expand Up @@ -498,11 +499,11 @@ private function addOrmSection(ArrayNodeDefinition $node): void
->validate()
->ifString()
->then(static function ($v) {
return constant('Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_' . strtoupper($v));
return constant('Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_' . strtoupper($v));
})
->end()
->end()
->booleanNode('enable_lazy_ghost_objects')->defaultFalse()
->booleanNode('enable_lazy_ghost_objects')->defaultValue(! method_exists(ProxyFactory::class, 'resetUninitializedProxy'))
->end()
->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end()
->scalarNode('proxy_namespace')->defaultValue('Proxies')->end()
Expand Down Expand Up @@ -832,7 +833,7 @@ private function getAutoGenerateModes(): array
{
$constPrefix = 'AUTOGENERATE_';
$prefixLen = strlen($constPrefix);
$refClass = new ReflectionClass(AbstractProxyFactory::class);
$refClass = new ReflectionClass(ProxyFactory::class);
$constsArray = $refClass->getConstants();
$namesArray = [];
$valuesArray = [];
Expand Down
24 changes: 10 additions & 14 deletions DependencyInjection/DoctrineExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,19 @@
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;
use Doctrine\DBAL\Schema\LegacySchemaManagerFactory;
use Doctrine\ORM\Configuration as OrmConfiguration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Events;
use Doctrine\ORM\Id\AbstractIdGenerator;
use Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver;
use Doctrine\ORM\Proxy\Autoloader;
use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand;
use Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand;
use Doctrine\ORM\Tools\Export\ClassMetadataExporter;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Persistence\Reflection\RuntimeReflectionProperty;
use InvalidArgumentException;
use LogicException;
use ReflectionMethod;
use Symfony\Bridge\Doctrine\ArgumentResolver\EntityValueResolver;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
Expand Down Expand Up @@ -75,6 +74,9 @@
use function sprintf;
use function str_replace;
use function trait_exists;
use function trigger_deprecation;

use const PHP_VERSION_ID;

/**
* DoctrineExtension is an extension for the Doctrine DBAL and ORM library.
Expand Down Expand Up @@ -438,11 +440,6 @@ protected function ormLoad(array $config, ContainerBuilder $container)
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('orm.xml');

if (! (new ReflectionMethod(EntityManager::class, '__construct'))->isPublic()) {
$container->getDefinition('doctrine.orm.entity_manager.abstract')
->setFactory(['%doctrine.orm.entity_manager.class%', 'create']);
}

if (class_exists(AbstractType::class)) {
$container->getDefinition('form.type.entity')->addTag('kernel.reset', ['method' => 'reset']);
}
Expand Down Expand Up @@ -545,13 +542,6 @@ protected function ormLoad(array $config, ContainerBuilder $container)
$container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']);

if ($config['enable_lazy_ghost_objects'] ?? false) {
if (! method_exists(OrmConfiguration::class, 'setLazyGhostObjectEnabled')) {
throw new LogicException(
'Lazy ghost objects cannot be enabled because the "doctrine/orm" library'
. ' version 2.14 or higher is not installed. Please run "composer update doctrine/orm".',
);
}

// available in Symfony 6.2 and higher
/** @psalm-suppress UndefinedClass */
if (! trait_exists(LazyGhostTrait::class)) {
Expand All @@ -567,6 +557,12 @@ protected function ormLoad(array $config, ContainerBuilder $container)
. ' version 3.1 or higher is not installed. Please run "composer update doctrine/persistence".',
);
}
} elseif (! method_exists(ProxyFactory::class, 'resetUninitializedProxy')) {
throw new LogicException(
'Lazy ghost objects cannot be disabled for ORM 3.',
);
} elseif (PHP_VERSION_ID >= 80100) {
trigger_deprecation('doctrine/doctrine-bundle', '2.11', 'Not setting "enable_lazy_ghost_objects" to true is deprecated.');
}

$options = ['auto_generate_proxy_classes', 'enable_lazy_ghost_objects', 'proxy_dir', 'proxy_namespace'];
Expand Down
9 changes: 4 additions & 5 deletions Mapping/ContainerEntityListenerResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function __construct(ContainerInterface $container)
/**
* {@inheritDoc}
*/
public function clear($className = null)
public function clear($className = null): void
{
if ($className === null) {
$this->instances = [];
Expand All @@ -48,7 +48,7 @@ public function clear($className = null)
/**
* {@inheritDoc}
*/
public function register($object)
public function register($object): void
{
if (! is_object($object)) {
throw new InvalidArgumentException(sprintf('An object was expected, but got "%s".', gettype($object)));
Expand All @@ -70,7 +70,7 @@ public function registerService($className, $serviceId)
/**
* {@inheritDoc}
*/
public function resolve($className)
public function resolve($className): object
{
$className = $this->normalizeClassName($className);

Expand All @@ -85,8 +85,7 @@ public function resolve($className)
return $this->instances[$className];
}

/** @return object */
private function resolveService(string $serviceId)
private function resolveService(string $serviceId): object
{
if (! $this->container->has($serviceId)) {
throw new RuntimeException(sprintf('There is no service named "%s"', $serviceId));
Expand Down
8 changes: 4 additions & 4 deletions Mapping/MappingDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Doctrine\Bundle\DoctrineBundle\Mapping;

use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Mapping\ClassMetadata as OrmClassMetadata;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\Driver\MappingDriver as MappingDriverInterface;
use Psr\Container\ContainerInterface;
Expand Down Expand Up @@ -42,8 +42,8 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void
$this->driver->loadMetadataForClass($className, $metadata);

if (
! $metadata instanceof ClassMetadataInfo
|| $metadata->generatorType !== ClassMetadataInfo::GENERATOR_TYPE_CUSTOM
! $metadata instanceof OrmClassMetadata
|| $metadata->generatorType !== OrmClassMetadata::GENERATOR_TYPE_CUSTOM
|| ! isset($metadata->customGeneratorDefinition['class'])
|| ! $this->idGeneratorLocator->has($metadata->customGeneratorDefinition['class'])
) {
Expand All @@ -52,7 +52,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void

$idGenerator = $this->idGeneratorLocator->get($metadata->customGeneratorDefinition['class']);
$metadata->setCustomGeneratorDefinition(['instance' => $idGenerator] + $metadata->customGeneratorDefinition);
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_NONE);
$metadata->setIdGeneratorType(OrmClassMetadata::GENERATOR_TYPE_NONE);
}

/**
Expand Down
22 changes: 19 additions & 3 deletions Repository/ContainerRepositoryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,27 @@

use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Repository\RepositoryFactory;
use Doctrine\Persistence\ObjectRepository;
use Psr\Container\ContainerInterface;
use RuntimeException;

use function class_exists;
use function get_debug_type;
use function is_a;
use function spl_object_hash;
use function sprintf;
use function trigger_deprecation;

/**
* Fetches repositories from the container or falls back to normal creation.
*/
final class ContainerRepositoryFactory implements RepositoryFactory
{
use RepositoryFactoryCompatibility;

/** @var array<string, ObjectRepository> */
private array $managedRepositories = [];

Expand All @@ -32,11 +37,14 @@
}

/**
* {@inheritDoc}
* @param class-string<T> $entityName
*
* @return ObjectRepository<T>
* @psalm-return ($strictTypeCheck is true ? EntityRepository<T> : ObjectRepository<T>)
*
* @template T of object
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository
private function doGetRepository(EntityManagerInterface $entityManager, string $entityName, bool $strictTypeCheck): ObjectRepository
{
$metadata = $entityManager->getClassMetadata($entityName);
$repositoryServiceId = $metadata->customRepositoryClassName;
Expand All @@ -47,8 +55,16 @@
if ($this->container->has($customRepositoryName)) {
$repository = $this->container->get($customRepositoryName);

if (! $repository instanceof EntityRepository && $strictTypeCheck) {
throw new RuntimeException(sprintf('The service "%s" must extend EntityRepository (e.g. by extending ServiceEntityRepository), "%s" given.', $repositoryServiceId, get_debug_type($repository)));
}

if (! $repository instanceof ObjectRepository) {
throw new RuntimeException(sprintf('The service "%s" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository).', $repositoryServiceId));
throw new RuntimeException(sprintf('The service "%s" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository), "%s" given.', $repositoryServiceId, get_debug_type($repository)));
}

if (! $repository instanceof EntityRepository) {
trigger_deprecation('doctrine/doctrine-bundle', '2.11', 'The service "%s" of type "%s" should extend "%s", not doing so is deprecated.', $repositoryServiceId, get_debug_type($repository), EntityRepository::class);

Check warning on line 67 in Repository/ContainerRepositoryFactory.php

View check run for this annotation

Codecov / codecov/patch

Repository/ContainerRepositoryFactory.php#L67

Added line #L67 was not covered by tests
}

/** @psalm-var ObjectRepository<T> */
Expand Down
15 changes: 1 addition & 14 deletions Repository/LazyServiceEntityRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,7 @@
use function sprintf;

/**
* Optional EntityRepository base class with a simplified constructor (for autowiring).
*
* To use in your class, inject the "registry" service and call
* the parent constructor. For example:
*
* class YourEntityRepository extends ServiceEntityRepository
* {
* public function __construct(ManagerRegistry $registry)
* {
* parent::__construct($registry, YourEntity::class);
* }
* }
*
* @internal to be renamed ServiceEntityRepository when PHP 8.1 / Symfony 6.2 becomes required
* @internal Extend {@see ServiceEntityRepository} instead.
*
* @template T of object
* @template-extends EntityRepository<T>
Expand Down
38 changes: 38 additions & 0 deletions Repository/LegacyServiceEntityRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Doctrine\Bundle\DoctrineBundle\Repository;

use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use LogicException;

use function sprintf;

/**
* @internal Extend {@see ServiceEntityRepository} instead.
*
* @template T of object
* @template-extends EntityRepository<T>
*/
class LegacyServiceEntityRepository extends EntityRepository implements ServiceEntityRepositoryInterface
{
/**
* @param string $entityClass The class name of the entity this repository manages
* @psalm-param class-string<T> $entityClass
*/
public function __construct(ManagerRegistry $registry, string $entityClass)

Check warning on line 25 in Repository/LegacyServiceEntityRepository.php

View check run for this annotation

Codecov / codecov/patch

Repository/LegacyServiceEntityRepository.php#L25

Added line #L25 was not covered by tests
{
$manager = $registry->getManagerForClass($entityClass);

Check warning on line 27 in Repository/LegacyServiceEntityRepository.php

View check run for this annotation

Codecov / codecov/patch

Repository/LegacyServiceEntityRepository.php#L27

Added line #L27 was not covered by tests

if ($manager === null) {
throw new LogicException(sprintf(
'Could not find the entity manager for class "%s". Check your Doctrine configuration to make sure it is configured to load this entity’s metadata.',
$entityClass,
));

Check warning on line 33 in Repository/LegacyServiceEntityRepository.php

View check run for this annotation

Codecov / codecov/patch

Repository/LegacyServiceEntityRepository.php#L29-L33

Added lines #L29 - L33 were not covered by tests
}

parent::__construct($manager, $manager->getClassMetadata($entityClass));

Check warning on line 36 in Repository/LegacyServiceEntityRepository.php

View check run for this annotation

Codecov / codecov/patch

Repository/LegacyServiceEntityRepository.php#L36

Added line #L36 was not covered by tests
}
}
43 changes: 43 additions & 0 deletions Repository/RepositoryFactoryCompatibility.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Repository;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Repository\RepositoryFactory;
use Doctrine\Persistence\ObjectRepository;
use ReflectionMethod;

if ((new ReflectionMethod(RepositoryFactory::class, 'getRepository'))->hasReturnType()) {
derrabus marked this conversation as resolved.
Show resolved Hide resolved
// ORM >= 3
/** @internal */
trait RepositoryFactoryCompatibility
{
/**
* Gets the repository for an entity class.
*
* @param class-string<T> $entityName
*
* @return EntityRepository<T>
*
* @template T of object
*
* @psalm-suppress MethodSignatureMismatch
*/
public function getRepository(EntityManagerInterface $entityManager, string $entityName): EntityRepository
{
return $this->doGetRepository($entityManager, $entityName, true);
}
}
} else {
// ORM 2
/** @internal */
trait RepositoryFactoryCompatibility
{
/** {@inheritDoc} */
public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository
{
return $this->doGetRepository($entityManager, $entityName, false);
}
}
}
Loading
Loading