Skip to content

Commit

Permalink
Merge pull request #11823 from doctrine/3.3.x-merge-up-into-3.4.x_lzh…
Browse files Browse the repository at this point in the history
…u6IBq

Merge release 3.3.2 into 3.4.x
  • Loading branch information
greg0ire authored Feb 5, 2025
2 parents 4163efd + c9557c5 commit ae74be5
Show file tree
Hide file tree
Showing 22 changed files with 304 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/coding-standards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ on:

jobs:
coding-standards:
uses: "doctrine/.github/.github/workflows/coding-standards.yml@7.1.0"
uses: "doctrine/.github/.github/workflows/coding-standards.yml@7.2.1"
2 changes: 1 addition & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ on:
jobs:
documentation:
name: "Documentation"
uses: "doctrine/.github/.github/workflows/documentation.yml@7.1.0"
uses: "doctrine/.github/.github/workflows/documentation.yml@7.2.1"
2 changes: 1 addition & 1 deletion .github/workflows/release-on-milestone-closed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
release:
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@7.1.0"
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@7.2.1"
secrets:
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}
Expand Down
7 changes: 3 additions & 4 deletions docs/en/reference/association-mapping.rst
Original file line number Diff line number Diff line change
Expand Up @@ -903,8 +903,7 @@ defaults to "id", just as in one-to-one or many-to-one mappings.

Additionally, when using typed properties with Doctrine 2.9 or newer
you can skip ``targetEntity`` in ``ManyToOne`` and ``OneToOne``
associations as they will be set based on type. Also ``nullable``
attribute on ``JoinColumn`` will be inherited from PHP type. So that:
associations as they will be set based on type. So that:

.. configuration-block::

Expand All @@ -931,7 +930,7 @@ Is essentially the same as following:
<?php
/** One Product has One Shipment. */
#[OneToOne(targetEntity: Shipment::class)]
#[JoinColumn(name: 'shipment_id', referencedColumnName: 'id', nullable: false)]
#[JoinColumn(name: 'shipment_id', referencedColumnName: 'id')]
private Shipment $shipment;
.. code-block:: annotation
Expand All @@ -940,7 +939,7 @@ Is essentially the same as following:
/**
* One Product has One Shipment.
* @OneToOne(targetEntity="Shipment")
* @JoinColumn(name="shipment_id", referencedColumnName="id", nullable=false)
* @JoinColumn(name="shipment_id", referencedColumnName="id")
*/
private Shipment $shipment;
Expand Down
5 changes: 4 additions & 1 deletion src/Cache/CollectionCacheKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ public function __construct(
public readonly string $entityClass,
public readonly string $association,
array $ownerIdentifier,
string $filterHash = '',
) {
ksort($ownerIdentifier);

$this->ownerIdentifier = $ownerIdentifier;

parent::__construct(str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association);
$filterHash = $filterHash === '' ? '' : '_' . $filterHash;

parent::__construct(str_replace('\\', '.', strtolower($entityClass)) . '_' . implode(' ', $ownerIdentifier) . '__' . $association . $filterHash);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\Collection\CollectionPersister;
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
use Doctrine\ORM\Query\FilterCollection;
use Doctrine\ORM\UnitOfWork;

use function array_values;
Expand All @@ -35,6 +36,7 @@ abstract class AbstractCollectionPersister implements CachedCollectionPersister
protected array $queuedCache = [];

protected string $regionName;
protected FilterCollection $filters;
protected CollectionHydrator $hydrator;
protected CacheLogger|null $cacheLogger;

Expand All @@ -48,6 +50,10 @@ public function __construct(
$cacheConfig = $configuration->getSecondLevelCacheConfiguration();
$cacheFactory = $cacheConfig->getCacheFactory();

$this->region = $region;
$this->persister = $persister;
$this->association = $association;
$this->filters = $em->getFilters();
$this->regionName = $region->getName();
$this->uow = $em->getUnitOfWork();
$this->metadataFactory = $em->getMetadataFactory();
Expand Down Expand Up @@ -135,7 +141,7 @@ public function containsKey(PersistentCollection $collection, mixed $key): bool
public function count(PersistentCollection $collection): int
{
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
$entry = $this->region->get($key);

if ($entry !== null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function afterTransactionRolledBack(): void
public function delete(PersistentCollection $collection): void
{
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());

$this->persister->delete($collection);

Expand All @@ -53,7 +53,7 @@ public function update(PersistentCollection $collection): void
}

$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());

// Invalidate non initialized collections OR ordered collection
if ($isDirty && ! $isInitialized || $this->association->isOrdered()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function afterTransactionRolledBack(): void
public function delete(PersistentCollection $collection): void
{
$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
$lock = $this->region->lock($key);

$this->persister->delete($collection);
Expand All @@ -88,7 +88,7 @@ public function update(PersistentCollection $collection): void
$this->persister->update($collection);

$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId);
$key = new CollectionCacheKey($this->sourceEntity->rootEntityName, $this->association->fieldName, $ownerId, $this->filters->getHash());
$lock = $this->region->lock($key);

if ($lock === null) {
Expand Down
15 changes: 11 additions & 4 deletions src/Cache/Persister/Entity/AbstractEntityPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\Entity\EntityPersister;
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
use Doctrine\ORM\Query\FilterCollection;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\UnitOfWork;

use function array_merge;
use function func_get_args;
use function serialize;
use function sha1;

Expand All @@ -43,6 +45,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
protected TimestampCacheKey $timestampKey;
protected EntityHydrator $hydrator;
protected Cache $cache;
protected FilterCollection $filters;
protected CacheLogger|null $cacheLogger = null;
protected string $regionName;

Expand All @@ -64,6 +67,7 @@ public function __construct(
$cacheFactory = $cacheConfig->getCacheFactory();

$this->cache = $em->getCache();
$this->filters = $em->getFilters();
$this->regionName = $region->getName();
$this->uow = $em->getUnitOfWork();
$this->metadataFactory = $em->getMetadataFactory();
Expand Down Expand Up @@ -215,7 +219,7 @@ protected function getHash(
? $this->persister->expandCriteriaParameters($criteria)
: $this->persister->expandParameters($criteria);

return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $this->filters->getHash());
}

/**
Expand Down Expand Up @@ -472,7 +476,7 @@ public function loadManyToManyCollection(
}

$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = $this->buildCollectionCacheKey($assoc, $ownerId);
$key = $this->buildCollectionCacheKey($assoc, $ownerId, $this->filters->getHash());
$list = $persister->loadCollectionCache($collection, $key);

if ($list !== null) {
Expand Down Expand Up @@ -503,7 +507,7 @@ public function loadOneToManyCollection(
}

$ownerId = $this->uow->getEntityIdentifier($collection->getOwner());
$key = $this->buildCollectionCacheKey($assoc, $ownerId);
$key = $this->buildCollectionCacheKey($assoc, $ownerId, $this->filters->getHash());
$list = $persister->loadCollectionCache($collection, $key);

if ($list !== null) {
Expand Down Expand Up @@ -546,12 +550,15 @@ public function refresh(array $id, object $entity, LockMode|int|null $lockMode =
}

/** @param array<string, mixed> $ownerId */
protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId): CollectionCacheKey
protected function buildCollectionCacheKey(AssociationMapping $association, array $ownerId, /* string $filterHash */): CollectionCacheKey
{
$filterHash = (string) (func_get_args()[2] ?? ''); // todo: move to argument in next major release

return new CollectionCacheKey(
$this->metadataFactory->getMetadataFor($association->sourceEntity)->rootEntityName,
$association->fieldName,
$ownerId,
$filterHash,
);
}
}
14 changes: 11 additions & 3 deletions src/Internal/Hydration/SimpleObjectHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use function assert;
use function count;
use function in_array;
use function is_array;
use function key;
use function reset;
use function sprintf;
Expand Down Expand Up @@ -138,14 +139,21 @@ protected function hydrateRowData(array $row, array &$result): void
}

if ($value !== null && isset($cacheKeyInfo['enumType'])) {
$originalValue = $value;
$originalValue = $currentValue = $value;
try {
$value = $this->buildEnum($originalValue, $cacheKeyInfo['enumType']);
if (! is_array($originalValue)) {
$value = $this->buildEnum($originalValue, $cacheKeyInfo['enumType']);
} else {
$value = [];
foreach ($originalValue as $i => $currentValue) {
$value[$i] = $this->buildEnum($currentValue, $cacheKeyInfo['enumType']);
}
}
} catch (ValueError $e) {
throw MappingException::invalidEnumValue(
$entityName,
$cacheKeyInfo['fieldName'],
(string) $originalValue,
(string) $currentValue,
$cacheKeyInfo['enumType'],
$e,
);
Expand Down
14 changes: 7 additions & 7 deletions src/Mapping/DefaultQuoteStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class DefaultQuoteStrategy implements QuoteStrategy
public function getColumnName(string $fieldName, ClassMetadata $class, AbstractPlatform $platform): string
{
return isset($class->fieldMappings[$fieldName]->quoted)
? $platform->quoteIdentifier($class->fieldMappings[$fieldName]->columnName)
? $platform->quoteSingleIdentifier($class->fieldMappings[$fieldName]->columnName)
: $class->fieldMappings[$fieldName]->columnName;
}

Expand All @@ -42,7 +42,7 @@ public function getTableName(ClassMetadata $class, AbstractPlatform $platform):
}

return isset($class->table['quoted'])
? $platform->quoteIdentifier($tableName)
? $platform->quoteSingleIdentifier($tableName)
: $tableName;
}

Expand All @@ -52,14 +52,14 @@ public function getTableName(ClassMetadata $class, AbstractPlatform $platform):
public function getSequenceName(array $definition, ClassMetadata $class, AbstractPlatform $platform): string
{
return isset($definition['quoted'])
? $platform->quoteIdentifier($definition['sequenceName'])
? $platform->quoteSingleIdentifier($definition['sequenceName'])
: $definition['sequenceName'];
}

public function getJoinColumnName(JoinColumnMapping $joinColumn, ClassMetadata $class, AbstractPlatform $platform): string
{
return isset($joinColumn->quoted)
? $platform->quoteIdentifier($joinColumn->name)
? $platform->quoteSingleIdentifier($joinColumn->name)
: $joinColumn->name;
}

Expand All @@ -69,7 +69,7 @@ public function getReferencedJoinColumnName(
AbstractPlatform $platform,
): string {
return isset($joinColumn->quoted)
? $platform->quoteIdentifier($joinColumn->referencedColumnName)
? $platform->quoteSingleIdentifier($joinColumn->referencedColumnName)
: $joinColumn->referencedColumnName;
}

Expand All @@ -87,7 +87,7 @@ public function getJoinTableName(
$tableName = $association->joinTable->name;

if (isset($association->joinTable->quoted)) {
$tableName = $platform->quoteIdentifier($tableName);
$tableName = $platform->quoteSingleIdentifier($tableName);
}

return $schema . $tableName;
Expand All @@ -113,7 +113,7 @@ public function getIdentifierColumnNames(ClassMetadata $class, AbstractPlatform
$joinColumns = $assoc->joinColumns;
$assocQuotedColumnNames = array_map(
static fn (JoinColumnMapping $joinColumn) => isset($joinColumn->quoted)
? $platform->quoteIdentifier($joinColumn->name)
? $platform->quoteSingleIdentifier($joinColumn->name)
: $joinColumn->name,
$joinColumns,
);
Expand Down
5 changes: 5 additions & 0 deletions src/Mapping/Driver/ReflectionBasedDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@ trait ReflectionBasedDriver
*/
private function isRepeatedPropertyDeclaration(ReflectionProperty $property, ClassMetadata $metadata): bool
{
/** @var class-string $declaringClass */
$declaringClass = $property->class;

if ($this->isTransient($declaringClass)) {
return isset($metadata->fieldMappings[$property->name]);
}

if (
isset($metadata->fieldMappings[$property->name]->declared)
&& $metadata->fieldMappings[$property->name]->declared === $declaringClass
Expand Down
10 changes: 9 additions & 1 deletion src/Persisters/Entity/BasicEntityPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -1500,7 +1500,15 @@ protected function getSelectColumnSQL(string $field, ClassMetadata $class, strin
$tableAlias = $this->getSQLTableAlias($class->name, $root);
$fieldMapping = $class->fieldMappings[$field];
$sql = sprintf('%s.%s', $tableAlias, $this->quoteStrategy->getColumnName($field, $class, $this->platform));
$columnAlias = $this->getSQLColumnAlias($fieldMapping->columnName);

$columnAlias = null;
if ($this->currentPersisterContext->rsm->hasColumnAliasByField($alias, $field)) {
$columnAlias = $this->currentPersisterContext->rsm->getColumnAliasByField($alias, $field);
}

if ($columnAlias === null) {
$columnAlias = $this->getSQLColumnAlias($fieldMapping->columnName);
}

$this->currentPersisterContext->rsm->addFieldResult($alias, $columnAlias, $field);
if (! empty($fieldMapping->enumType)) {
Expand Down
26 changes: 25 additions & 1 deletion src/Query/ResultSetMapping.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ class ResultSetMapping
*/
public array $fieldMappings = [];

/**
* Map field names for each class to alias
*
* @var array<class-string, array<string, array<string, string>>>
*/
public array $columnAliasMappings = [];

/**
* Maps column names in the result set to the alias/field name to use in the mapped result.
*
Expand Down Expand Up @@ -328,7 +335,10 @@ public function addFieldResult(string $alias, string $columnName, string $fieldN
// column name => alias of owner
$this->columnOwnerMap[$columnName] = $alias;
// field name => class name of declaring class
$this->declaringClasses[$columnName] = $declaringClass ?: $this->aliasMap[$alias];
$declaringClass = $declaringClass ?: $this->aliasMap[$alias];
$this->declaringClasses[$columnName] = $declaringClass;

$this->columnAliasMappings[$declaringClass][$alias][$fieldName] = $columnName;

if (! $this->isMixed && $this->scalarMappings) {
$this->isMixed = true;
Expand All @@ -337,6 +347,20 @@ public function addFieldResult(string $alias, string $columnName, string $fieldN
return $this;
}

public function hasColumnAliasByField(string $alias, string $fieldName): bool
{
$declaringClass = $this->aliasMap[$alias];

return isset($this->columnAliasMappings[$declaringClass][$alias][$fieldName]);
}

public function getColumnAliasByField(string $alias, string $fieldName): string
{
$declaringClass = $this->aliasMap[$alias];

return $this->columnAliasMappings[$declaringClass][$alias][$fieldName];
}

/**
* Adds a joined entity result.
*
Expand Down
Loading

0 comments on commit ae74be5

Please sign in to comment.