Skip to content

Commit

Permalink
Merge branch '2.12.x' into 3.0.x
Browse files Browse the repository at this point in the history
* 2.12.x:
  Document QueryComponent array shape (doctrine#9527)
  Improve templating
  Un-deprecate the current proxy mechanism (doctrine#9532)
  Remove unused methods
  Fix bug-doctrine#9536
  • Loading branch information
derrabus committed Feb 25, 2022
2 parents 5ebe984 + 7be96f6 commit 4292d8e
Show file tree
Hide file tree
Showing 24 changed files with 341 additions and 247 deletions.
17 changes: 17 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,23 @@ Use `toIterable()` instead.

# Upgrade to 2.12

## Un-deprecate `Doctrine\ORM\Proxy\Proxy`

Because no forward-compatible new proxy solution had been implemented yet, the
current proxy mechanism is not considered deprecated anymore for the time
being. This applies to the following interfaces/classes:

* `Doctrine\ORM\Proxy\Proxy`
* `Doctrine\ORM\Proxy\ProxyFactory`

These methods have been un-deprecated:

* `Doctrine\ORM\Configuration::getAutoGenerateProxyClasses()`
* `Doctrine\ORM\Configuration::getProxyDir()`
* `Doctrine\ORM\Configuration::getProxyNamespace()`

Note that the `Doctrine\ORM\Proxy\Autoloader` remains deprecated and will be removed in 3.0.

## Deprecate helper methods from `AbstractCollectionPersister`

The following protected methods of
Expand Down
16 changes: 4 additions & 12 deletions lib/Doctrine/ORM/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Doctrine\ORM\Mapping\EntityListenerResolver;
use Doctrine\ORM\Mapping\NamingStrategy;
use Doctrine\ORM\Mapping\QuoteStrategy;
use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\ORM\Query\Filter\SQLFilter;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
use Doctrine\ORM\Repository\RepositoryFactory;
Expand All @@ -33,6 +34,8 @@
* It combines all configuration options from DBAL & ORM.
*
* Internal note: When adding a new configuration option just write a getter/setter pair.
*
* @psalm-import-type AutogenerateMode from ProxyFactory
*/
class Configuration extends \Doctrine\DBAL\Configuration
{
Expand All @@ -54,10 +57,6 @@ public function setProxyDir($dir)
/**
* Gets the directory where Doctrine generates any necessary proxy class files.
*
* @deprecated 2.7 We're switch to `ocramius/proxy-manager` and this method isn't applicable any longer
*
* @see https://github.com/Ocramius/ProxyManager
*
* @return string|null
*/
public function getProxyDir()
Expand All @@ -68,11 +67,8 @@ public function getProxyDir()
/**
* Gets the strategy for automatically generating proxy classes.
*
* @deprecated 2.7 We're switch to `ocramius/proxy-manager` and this method isn't applicable any longer
*
* @see https://github.com/Ocramius/ProxyManager
*
* @return int Possible values are constants of Doctrine\Common\Proxy\AbstractProxyFactory.
* @psalm-return AutogenerateMode
*/
public function getAutoGenerateProxyClasses()
{
Expand All @@ -95,10 +91,6 @@ public function setAutoGenerateProxyClasses($autoGenerate)
/**
* Gets the namespace where proxy classes reside.
*
* @deprecated 2.7 We're switch to `ocramius/proxy-manager` and this method isn't applicable any longer
*
* @see https://github.com/Ocramius/ProxyManager
*
* @return string|null
*/
public function getProxyNamespace()
Expand Down
16 changes: 12 additions & 4 deletions lib/Doctrine/ORM/Mapping/Driver/AttributeDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,20 @@ class AttributeDriver extends AnnotationDriver
Mapping\MappedSuperclass::class => 2,
];

/**
* The annotation reader.
*
* @var AttributeReader
*/
protected $reader;

/**
* @param array<string> $paths
*/
public function __construct(array $paths)
{
parent::__construct(new AttributeReader(), $paths);
$this->reader = new AttributeReader();
$this->addPaths($paths);
}

/**
Expand Down Expand Up @@ -260,7 +268,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void
// Check for JoinColumn/JoinColumns annotations
$joinColumns = [];

$joinColumnAttributes = $this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class);
$joinColumnAttributes = $this->reader->getPropertyAnnotationCollection($property, Mapping\JoinColumn::class);

foreach ($joinColumnAttributes as $joinColumnAttribute) {
$joinColumns[] = $this->joinColumnToArray($joinColumnAttribute);
Expand Down Expand Up @@ -365,11 +373,11 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void
];
}

foreach ($this->reader->getPropertyAnnotation($property, Mapping\JoinColumn::class) as $joinColumn) {
foreach ($this->reader->getPropertyAnnotationCollection($property, Mapping\JoinColumn::class) as $joinColumn) {
$joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
}

foreach ($this->reader->getPropertyAnnotation($property, Mapping\InverseJoinColumn::class) as $joinColumn) {
foreach ($this->reader->getPropertyAnnotationCollection($property, Mapping\InverseJoinColumn::class) as $joinColumn) {
$joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
}

Expand Down
79 changes: 59 additions & 20 deletions lib/Doctrine/ORM/Mapping/Driver/AttributeReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Attribute;
use Doctrine\ORM\Mapping\Annotation;
use LogicException;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;
Expand All @@ -14,58 +15,93 @@
use function assert;
use function is_string;
use function is_subclass_of;
use function sprintf;

/**
* @internal
*/
final class AttributeReader
{
/** @var array<string,bool> */
/** @var array<class-string<Annotation>,bool> */
private array $isRepeatableAttribute = [];

/** @return array<Annotation|RepeatableAttributeCollection> */
/**
* @psalm-return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
public function getClassAnnotations(ReflectionClass $class): array
{
return $this->convertToAttributeInstances($class->getAttributes());
}

/** @return Annotation|RepeatableAttributeCollection|null */
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
return $this->getClassAnnotations($class)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}

/** @return array<Annotation|RepeatableAttributeCollection> */
/**
* @return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
public function getMethodAnnotations(ReflectionMethod $method): array
{
return $this->convertToAttributeInstances($method->getAttributes());
}

/** @return Annotation|RepeatableAttributeCollection|null */
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
return $this->getMethodAnnotations($method)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}

/** @return array<Annotation|RepeatableAttributeCollection> */
/**
* @return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
public function getPropertyAnnotations(ReflectionProperty $property): array
{
return $this->convertToAttributeInstances($property->getAttributes());
}

/** @return Annotation|RepeatableAttributeCollection|null */
/**
* @param class-string<T> $annotationName The name of the annotation.
*
* @return T|null
*
* @template T of Annotation
*/
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
if ($this->isRepeatable($annotationName)) {
throw new LogicException(sprintf(
'The attribute "%s" is repeatable. Call getPropertyAnnotationCollection() instead.',
$annotationName
));
}

return $this->getPropertyAnnotations($property)[$annotationName]
?? ($this->isRepeatable($annotationName) ? new RepeatableAttributeCollection() : null);
}

/**
* @param class-string<T> $annotationName The name of the annotation.
*
* @return RepeatableAttributeCollection<T>
*
* @template T of Annotation
*/
public function getPropertyAnnotationCollection(
ReflectionProperty $property,
string $annotationName
): RepeatableAttributeCollection {
if (! $this->isRepeatable($annotationName)) {
throw new LogicException(sprintf(
'The attribute "%s" is not repeatable. Call getPropertyAnnotation() instead.',
$annotationName
));
}

return $this->getPropertyAnnotations($property)[$annotationName] ?? new RepeatableAttributeCollection();
}

/**
* @param array<ReflectionAttribute> $attributes
*
* @return array<Annotation|RepeatableAttributeCollection>
* @return class-string-map<T, T|RepeatableAttributeCollection<T>>
*
* @template T of Annotation
*/
private function convertToAttributeInstances(array $attributes): array
{
Expand Down Expand Up @@ -98,6 +134,9 @@ private function convertToAttributeInstances(array $attributes): array
return $instances;
}

/**
* @param class-string<Annotation> $attributeClassName
*/
private function isRepeatable(string $attributeClassName): bool
{
if (isset($this->isRepeatableAttribute[$attributeClassName])) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
use Doctrine\ORM\Mapping\Annotation;

/**
* @template-extends ArrayObject<int,Annotation>
* @template-extends ArrayObject<int, T>
* @template T of Annotation
*/
final class RepeatableAttributeCollection extends ArrayObject
{
Expand Down
2 changes: 0 additions & 2 deletions lib/Doctrine/ORM/Proxy/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

/**
* Interface for proxy classes.
*
* @deprecated 2.7 This interface is being removed from the ORM and won't have any replacement, proxies will no longer implement it.
*/
interface Proxy extends BaseProxy
{
Expand Down
5 changes: 3 additions & 2 deletions lib/Doctrine/ORM/Proxy/ProxyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
/**
* This factory is used to create proxy objects for entities at runtime.
*
* @deprecated 2.7 This class is being removed from the ORM and won't have any replacement
* @psalm-type AutogenerateMode = AbstractProxyFactory::AUTOGENERATE_NEVER|AbstractProxyFactory::AUTOGENERATE_ALWAYS|AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS|AbstractProxyFactory::AUTOGENERATE_EVAL|AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED
*/
class ProxyFactory extends AbstractProxyFactory
{
Expand Down Expand Up @@ -48,7 +48,8 @@ class ProxyFactory extends AbstractProxyFactory
* @param string $proxyDir The directory to use for the proxy classes. It must exist.
* @param string $proxyNs The namespace to use for the proxy classes.
* @param bool|int $autoGenerate The strategy for automatically generating proxy classes. Possible
* values are constants of Doctrine\Common\Proxy\AbstractProxyFactory.
* values are constants of {@see AbstractProxyFactory}.
* @psalm-param bool|AutogenerateMode $autoGenerate
*/
public function __construct(EntityManagerInterface $em, $proxyDir, $proxyNs, $autoGenerate = AbstractProxyFactory::AUTOGENERATE_NEVER)
{
Expand Down
17 changes: 9 additions & 8 deletions lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\SqlWalker;

use function assert;
use function reset;
use function sprintf;

Expand All @@ -23,22 +24,22 @@ class IdentityFunction extends FunctionNode
/** @var PathExpression */
public $pathExpression;

/** @var string */
/** @var string|null */
public $fieldMapping;

/**
* {@inheritdoc}
*/
public function getSql(SqlWalker $sqlWalker)
{
$platform = $sqlWalker->getEntityManager()->getConnection()->getDatabasePlatform();
$quoteStrategy = $sqlWalker->getEntityManager()->getConfiguration()->getQuoteStrategy();
assert($this->pathExpression->field !== null);
$entityManager = $sqlWalker->getEntityManager();
$platform = $entityManager->getConnection()->getDatabasePlatform();
$quoteStrategy = $entityManager->getConfiguration()->getQuoteStrategy();
$dqlAlias = $this->pathExpression->identificationVariable;
$assocField = $this->pathExpression->field;
$qComp = $sqlWalker->getQueryComponent($dqlAlias);
$class = $qComp['metadata'];
$assoc = $class->associationMappings[$assocField];
$targetEntity = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']);
$assoc = $sqlWalker->getMetadataForDqlAlias($dqlAlias)->associationMappings[$assocField];
$targetEntity = $entityManager->getClassMetadata($assoc['targetEntity']);
$joinColumn = reset($assoc['joinColumns']);

if ($this->fieldMapping !== null) {
Expand All @@ -63,7 +64,7 @@ public function getSql(SqlWalker $sqlWalker)
}

// The table with the relation may be a subclass, so get the table name from the association definition
$tableName = $sqlWalker->getEntityManager()->getClassMetadata($assoc['sourceEntity'])->getTableName();
$tableName = $entityManager->getClassMetadata($assoc['sourceEntity'])->getTableName();

$tableAlias = $sqlWalker->getSQLTableAlias($tableName, $dqlAlias);
$columnName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform);
Expand Down
15 changes: 9 additions & 6 deletions lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;

use function assert;

/**
* "SIZE" "(" CollectionValuedPathExpression ")"
*
Expand All @@ -27,18 +29,19 @@ class SizeFunction extends FunctionNode
*/
public function getSql(SqlWalker $sqlWalker)
{
$platform = $sqlWalker->getEntityManager()->getConnection()->getDatabasePlatform();
$quoteStrategy = $sqlWalker->getEntityManager()->getConfiguration()->getQuoteStrategy();
assert($this->collectionPathExpression->field !== null);
$entityManager = $sqlWalker->getEntityManager();
$platform = $entityManager->getConnection()->getDatabasePlatform();
$quoteStrategy = $entityManager->getConfiguration()->getQuoteStrategy();
$dqlAlias = $this->collectionPathExpression->identificationVariable;
$assocField = $this->collectionPathExpression->field;

$qComp = $sqlWalker->getQueryComponent($dqlAlias);
$class = $qComp['metadata'];
$class = $sqlWalker->getMetadataForDqlAlias($dqlAlias);
$assoc = $class->associationMappings[$assocField];
$sql = 'SELECT COUNT(*) FROM ';

if ($assoc['type'] === ClassMetadata::ONE_TO_MANY) {
$targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']);
$targetClass = $entityManager->getClassMetadata($assoc['targetEntity']);
$targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName());
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);

Expand All @@ -60,7 +63,7 @@ public function getSql(SqlWalker $sqlWalker)
. $sourceTableAlias . '.' . $quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $platform);
}
} else { // many-to-many
$targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']);
$targetClass = $entityManager->getClassMetadata($assoc['targetEntity']);

$owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']];
$joinTable = $owningAssoc['joinTable'];
Expand Down
Loading

0 comments on commit 4292d8e

Please sign in to comment.