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

Merge release 3.3.2 into 3.4.x #11823

Merged
merged 14 commits into from
Feb 5, 2025
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
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
Loading