Skip to content

Commit

Permalink
Merge pull request #11432 from doctrine/2.19.x-merge-up-into-2.20.x_I…
Browse files Browse the repository at this point in the history
…fraK93L

Merge release 2.19.5 into 2.20.x
  • Loading branch information
greg0ire committed Apr 30, 2024
2 parents b725908 + 94986af commit 8b6a58f
Show file tree
Hide file tree
Showing 10 changed files with 342 additions and 72 deletions.
2 changes: 1 addition & 1 deletion docs/en/cookbook/validation-of-entities.rst
Expand Up @@ -11,7 +11,7 @@ What we offer are hooks to execute any kind of validation.
.. note::

You don't need to validate your entities in the lifecycle
events. Its only one of many options. Of course you can also
events. It is only one of many options. Of course you can also
perform validations in value setters or any other method of your
entities that are used in your code.

Expand Down
8 changes: 4 additions & 4 deletions docs/en/reference/association-mapping.rst
Expand Up @@ -1300,8 +1300,8 @@ This is essentially the same as the following, more verbose, mapping:
* @var Collection<int, Group>
*/
#[JoinTable(name: 'User_Group')]
#[JoinColumn(name: 'User_id', referencedColumnName: 'id')]
#[InverseJoinColumn(name: 'Group_id', referencedColumnName: 'id')]
#[JoinColumn(name: 'user_id', referencedColumnName: 'id')]
#[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')]
#[ManyToMany(targetEntity: Group::class)]
private Collection $groups;
// ...
Expand Down Expand Up @@ -1333,10 +1333,10 @@ This is essentially the same as the following, more verbose, mapping:
<many-to-many field="groups" target-entity="Group">
<join-table name="User_Group">
<join-columns>
<join-column id="User_id" referenced-column-name="id" />
<join-column id="user_id" referenced-column-name="id" />
</join-columns>
<inverse-join-columns>
<join-column id="Group_id" referenced-column-name="id" />
<join-column id="group_id" referenced-column-name="id" />
</inverse-join-columns>
</join-table>
</many-to-many>
Expand Down
34 changes: 18 additions & 16 deletions src/Internal/Hydration/AbstractHydrator.php
Expand Up @@ -182,29 +182,31 @@ public function toIterable($stmt, ResultSetMapping $resultSetMapping, array $hin

$this->prepare();

while (true) {
$row = $this->statement()->fetchAssociative();

if ($row === false) {
$this->cleanup();
try {
while (true) {
$row = $this->statement()->fetchAssociative();

break;
}
if ($row === false) {
break;
}

$result = [];
$result = [];

$this->hydrateRowData($row, $result);
$this->hydrateRowData($row, $result);

$this->cleanupAfterRowIteration();
if (count($result) === 1) {
if (count($resultSetMapping->indexByMap) === 0) {
yield end($result);
$this->cleanupAfterRowIteration();
if (count($result) === 1) {
if (count($resultSetMapping->indexByMap) === 0) {
yield end($result);
} else {
yield from $result;
}
} else {
yield from $result;
yield $result;
}
} else {
yield $result;
}
} finally {
$this->cleanup();
}
}

Expand Down
94 changes: 49 additions & 45 deletions src/ORMException.php
Expand Up @@ -5,11 +5,31 @@
namespace Doctrine\ORM;

use Doctrine\Common\Cache\Cache as CacheDriver;
use Doctrine\Persistence\ObjectRepository;
use Doctrine\ORM\Cache\Exception\InvalidResultCacheDriver;
use Doctrine\ORM\Cache\Exception\MetadataCacheNotConfigured;
use Doctrine\ORM\Cache\Exception\MetadataCacheUsesNonPersistentCache;
use Doctrine\ORM\Cache\Exception\QueryCacheNotConfigured;
use Doctrine\ORM\Cache\Exception\QueryCacheUsesNonPersistentCache;
use Doctrine\ORM\Exception\EntityManagerClosed;
use Doctrine\ORM\Exception\InvalidEntityRepository;
use Doctrine\ORM\Exception\InvalidHydrationMode;
use Doctrine\ORM\Exception\MismatchedEventManager;
use Doctrine\ORM\Exception\MissingIdentifierField;
use Doctrine\ORM\Exception\MissingMappingDriverImplementation;
use Doctrine\ORM\Exception\NamedNativeQueryNotFound;
use Doctrine\ORM\Exception\NamedQueryNotFound;
use Doctrine\ORM\Exception\ProxyClassesAlwaysRegenerating;
use Doctrine\ORM\Exception\UnexpectedAssociationValue;
use Doctrine\ORM\Exception\UnknownEntityNamespace;
use Doctrine\ORM\Exception\UnrecognizedIdentifierFields;
use Doctrine\ORM\Persisters\Exception\CantUseInOperatorOnCompositeKeys;
use Doctrine\ORM\Persisters\Exception\InvalidOrientation;
use Doctrine\ORM\Persisters\Exception\UnrecognizedField;
use Doctrine\ORM\Repository\Exception\InvalidFindByCall;
use Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall;
use Doctrine\ORM\Tools\Exception\NotSupported;
use Exception;

use function get_debug_type;
use function implode;
use function sprintf;

/**
Expand All @@ -26,8 +46,7 @@ class ORMException extends Exception
*/
public static function missingMappingDriverImpl()
{
return new self("It's a requirement to specify a Metadata Driver and pass it " .
'to Doctrine\\ORM\\Configuration::setMetadataDriverImpl().');
return MissingMappingDriverImplementation::create();
}

/**
Expand All @@ -39,19 +58,19 @@ public static function missingMappingDriverImpl()
*/
public static function namedQueryNotFound($queryName)
{
return new self('Could not find a named query by the name "' . $queryName . '"');
return NamedQueryNotFound::fromName($queryName);
}

/**
* @deprecated Use Doctrine\ORM\Exception\NamedQueryNotFound
* @deprecated Use Doctrine\ORM\Exception\NamedNativeQueryNotFound
*
* @param string $nativeQueryName
*
* @return ORMException
*/
public static function namedNativeQueryNotFound($nativeQueryName)
{
return new self('Could not find a named native query by the name "' . $nativeQueryName . '"');
return NamedNativeQueryNotFound::fromName($nativeQueryName);
}

/**
Expand All @@ -63,7 +82,7 @@ public static function namedNativeQueryNotFound($nativeQueryName)
*/
public static function unrecognizedField($field)
{
return new self(sprintf('Unrecognized field: %s', $field));
return new UnrecognizedField(sprintf('Unrecognized field: %s', $field));
}

/**
Expand All @@ -78,7 +97,7 @@ public static function unrecognizedField($field)
*/
public static function unexpectedAssociationValue($class, $association, $given, $expected)
{
return new self(sprintf('Found entity of type %s on association %s#%s, but expecting %s', $given, $class, $association, $expected));
return UnexpectedAssociationValue::create($class, $association, $given, $expected);
}

/**
Expand All @@ -91,7 +110,7 @@ public static function unexpectedAssociationValue($class, $association, $given,
*/
public static function invalidOrientation($className, $field)
{
return new self('Invalid order by orientation specified for ' . $className . '#' . $field);
return InvalidOrientation::fromClassNameAndField($className, $field);
}

/**
Expand All @@ -101,7 +120,7 @@ public static function invalidOrientation($className, $field)
*/
public static function entityManagerClosed()
{
return new self('The EntityManager is closed.');
return EntityManagerClosed::create();
}

/**
Expand All @@ -113,7 +132,7 @@ public static function entityManagerClosed()
*/
public static function invalidHydrationMode($mode)
{
return new self(sprintf("'%s' is an invalid hydration mode.", $mode));
return InvalidHydrationMode::fromMode($mode);
}

/**
Expand All @@ -123,7 +142,7 @@ public static function invalidHydrationMode($mode)
*/
public static function mismatchedEventManager()
{
return new self('Cannot use different EventManager instances for EntityManager and Connection.');
return MismatchedEventManager::create();
}

/**
Expand All @@ -135,11 +154,11 @@ public static function mismatchedEventManager()
*/
public static function findByRequiresParameter($methodName)
{
return new self("You need to pass a parameter to '" . $methodName . "'");
return InvalidMagicMethodCall::onMissingParameter($methodName);
}

/**
* @deprecated Doctrine\ORM\Repository\Exception\InvalidFindByCall
* @deprecated Doctrine\ORM\Repository\Exception\InvalidMagicMethodCall::becauseFieldNotFoundIn()
*
* @param string $entityName
* @param string $fieldName
Expand All @@ -149,10 +168,7 @@ public static function findByRequiresParameter($methodName)
*/
public static function invalidMagicCall($entityName, $fieldName, $method)
{
return new self(
"Entity '" . $entityName . "' has no field '" . $fieldName . "'. " .
"You can therefore not call '" . $method . "' on the entities' repository"
);
return InvalidMagicMethodCall::becauseFieldNotFoundIn($entityName, $fieldName, $method);
}

/**
Expand All @@ -165,10 +181,7 @@ public static function invalidMagicCall($entityName, $fieldName, $method)
*/
public static function invalidFindByInverseAssociation($entityName, $associationFieldName)
{
return new self(
"You cannot search for the association field '" . $entityName . '#' . $associationFieldName . "', " .
'because it is the inverse side of an association. Find methods only work on owning side associations.'
);
return InvalidFindByCall::fromInverseSideUsage($entityName, $associationFieldName);
}

/**
Expand All @@ -178,7 +191,7 @@ public static function invalidFindByInverseAssociation($entityName, $association
*/
public static function invalidResultCacheDriver()
{
return new self('Invalid result cache driver; it must implement Doctrine\\Common\\Cache\\Cache.');
return InvalidResultCacheDriver::create();
}

/**
Expand All @@ -188,7 +201,7 @@ public static function invalidResultCacheDriver()
*/
public static function notSupported()
{
return new self('This behaviour is (currently) not supported by Doctrine 2');
return NotSupported::create();
}

/**
Expand All @@ -198,7 +211,7 @@ public static function notSupported()
*/
public static function queryCacheNotConfigured()
{
return new self('Query Cache is not configured.');
return QueryCacheNotConfigured::create();
}

/**
Expand All @@ -208,7 +221,7 @@ public static function queryCacheNotConfigured()
*/
public static function metadataCacheNotConfigured()
{
return new self('Class Metadata Cache is not configured.');
return MetadataCacheNotConfigured::create();
}

/**
Expand All @@ -218,7 +231,7 @@ public static function metadataCacheNotConfigured()
*/
public static function queryCacheUsesNonPersistentCache(CacheDriver $cache)
{
return new self('Query Cache uses a non-persistent cache driver, ' . get_debug_type($cache) . '.');
return QueryCacheUsesNonPersistentCache::fromDriver($cache);
}

/**
Expand All @@ -228,7 +241,7 @@ public static function queryCacheUsesNonPersistentCache(CacheDriver $cache)
*/
public static function metadataCacheUsesNonPersistentCache(CacheDriver $cache)
{
return new self('Metadata Cache uses a non-persistent cache driver, ' . get_debug_type($cache) . '.');
return MetadataCacheUsesNonPersistentCache::fromDriver($cache);
}

/**
Expand All @@ -238,7 +251,7 @@ public static function metadataCacheUsesNonPersistentCache(CacheDriver $cache)
*/
public static function proxyClassesAlwaysRegenerating()
{
return new self('Proxy Classes are always regenerating.');
return ProxyClassesAlwaysRegenerating::create();
}

/**
Expand All @@ -250,9 +263,7 @@ public static function proxyClassesAlwaysRegenerating()
*/
public static function unknownEntityNamespace($entityNamespaceAlias)
{
return new self(
sprintf("Unknown Entity namespace alias '%s'.", $entityNamespaceAlias)
);
return UnknownEntityNamespace::fromNamespaceAlias($entityNamespaceAlias);
}

/**
Expand All @@ -264,11 +275,7 @@ public static function unknownEntityNamespace($entityNamespaceAlias)
*/
public static function invalidEntityRepository($className)
{
return new self(sprintf(
"Invalid repository class '%s'. It must be a %s.",
$className,
ObjectRepository::class
));
return InvalidEntityRepository::fromClassName($className);
}

/**
Expand All @@ -281,7 +288,7 @@ public static function invalidEntityRepository($className)
*/
public static function missingIdentifierField($className, $fieldName)
{
return new self(sprintf('The identifier %s is missing for a query of %s', $fieldName, $className));
return MissingIdentifierField::fromFieldAndClass($fieldName, $className);
}

/**
Expand All @@ -294,10 +301,7 @@ public static function missingIdentifierField($className, $fieldName)
*/
public static function unrecognizedIdentifierFields($className, $fieldNames)
{
return new self(
"Unrecognized identifier fields: '" . implode("', '", $fieldNames) . "' " .
"are not present on class '" . $className . "'."
);
return UnrecognizedIdentifierFields::fromClassAndFieldNames($className, $fieldNames);
}

/**
Expand All @@ -307,6 +311,6 @@ public static function unrecognizedIdentifierFields($className, $fieldNames)
*/
public static function cantUseInOperatorOnCompositeKeys()
{
return new self("Can't use IN operator on entities that have composite keys.");
return CantUseInOperatorOnCompositeKeys::create();
}
}
14 changes: 10 additions & 4 deletions src/UnitOfWork.php
Expand Up @@ -1292,6 +1292,8 @@ private function executeDeletions(): void
$eventsToDispatch = [];

foreach ($entities as $entity) {
$this->removeFromIdentityMap($entity);

$oid = spl_object_id($entity);
$class = $this->em->getClassMetadata(get_class($entity));
$persister = $this->getEntityPersister($class->name);
Expand Down Expand Up @@ -1667,8 +1669,6 @@ public function scheduleForDelete($entity)
return;
}

$this->removeFromIdentityMap($entity);

unset($this->entityUpdates[$oid]);

if (! isset($this->entityDeletions[$oid])) {
Expand Down Expand Up @@ -3224,7 +3224,13 @@ public function triggerEagerLoads()
*
* @param PersistentCollection[] $collections
* @param array<string, mixed> $mapping
* @psalm-param array{targetEntity: class-string, sourceEntity: class-string, mappedBy: string, indexBy: string|null} $mapping
* @psalm-param array{
* targetEntity: class-string,
* sourceEntity: class-string,
* mappedBy: string,
* indexBy: string|null,
* orderBy: array<string, string>|null
* } $mapping
*/
private function eagerLoadCollections(array $collections, array $mapping): void
{
Expand All @@ -3241,7 +3247,7 @@ private function eagerLoadCollections(array $collections, array $mapping): void
$entities[] = $collection->getOwner();
}

$found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities]);
$found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities], $mapping['orderBy'] ?? null);

$targetClass = $this->em->getClassMetadata($targetEntity);
$targetProperty = $targetClass->getReflectionProperty($mappedBy);
Expand Down

0 comments on commit 8b6a58f

Please sign in to comment.