mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 06:52:09 +01:00
Compare commits
36 Commits
old-protot
...
v2.6.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87ee409783 | ||
|
|
d47c1f3e9b | ||
|
|
b952dac339 | ||
|
|
ffb7d4c79c | ||
|
|
e68717b725 | ||
|
|
30a063ef9d | ||
|
|
35c3669ebc | ||
|
|
23f4f03575 | ||
|
|
a912fc09be | ||
|
|
a736a3713b | ||
|
|
f2da5bc93e | ||
|
|
2905b435db | ||
|
|
48ca6dbcec | ||
|
|
15a4302902 | ||
|
|
1f82a20312 | ||
|
|
fc943b70f6 | ||
|
|
f36470941c | ||
|
|
ae6d80daab | ||
|
|
44e82e2720 | ||
|
|
e94467d6da | ||
|
|
794c7708e8 | ||
|
|
8e73926359 | ||
|
|
8fc1d74820 | ||
|
|
496c6a9f03 | ||
|
|
7873f700b0 | ||
|
|
46c0861f45 | ||
|
|
5149c0ff25 | ||
|
|
cf99d62472 | ||
|
|
5878797eae | ||
|
|
8c2d090dc8 | ||
|
|
3f772eac32 | ||
|
|
62c952d258 | ||
|
|
c2f698e56e | ||
|
|
40f2a3efba | ||
|
|
333b9c0b99 | ||
|
|
90d19b4131 |
@@ -75,6 +75,10 @@ class DefaultEntityHydrator implements EntityHydrator
|
||||
$data = $this->uow->getOriginalEntityData($entity);
|
||||
$data = array_merge($data, $metadata->getIdentifierValues($entity)); // why update has no identifier values ?
|
||||
|
||||
if ($metadata->isVersioned) {
|
||||
$data[$metadata->versionField] = $metadata->getFieldValue($entity, $metadata->versionField);
|
||||
}
|
||||
|
||||
foreach ($metadata->associationMappings as $name => $assoc) {
|
||||
if ( ! isset($data[$name])) {
|
||||
continue;
|
||||
|
||||
@@ -24,6 +24,8 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Events;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use PDO;
|
||||
use function array_map;
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* Base class for all hydrators. A hydrator is a class that provides some form
|
||||
@@ -296,11 +298,8 @@ abstract class AbstractHydrator
|
||||
|
||||
// If there are field name collisions in the child class, then we need
|
||||
// to only hydrate if we are looking at the correct discriminator value
|
||||
if(
|
||||
isset($cacheKeyInfo['discriminatorColumn']) &&
|
||||
isset($data[$cacheKeyInfo['discriminatorColumn']]) &&
|
||||
// Note: loose comparison required. See https://github.com/doctrine/doctrine2/pull/6304#issuecomment-323294442
|
||||
$data[$cacheKeyInfo['discriminatorColumn']] != $cacheKeyInfo['discriminatorValue']
|
||||
if (isset($cacheKeyInfo['discriminatorColumn'], $data[$cacheKeyInfo['discriminatorColumn']])
|
||||
&& ! in_array((string) $data[$cacheKeyInfo['discriminatorColumn']], $cacheKeyInfo['discriminatorValues'], true)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
@@ -401,7 +400,8 @@ abstract class AbstractHydrator
|
||||
$columnInfo,
|
||||
[
|
||||
'discriminatorColumn' => $this->_rsm->discriminatorColumns[$ownerMap],
|
||||
'discriminatorValue' => $classMetadata->discriminatorValue
|
||||
'discriminatorValue' => $classMetadata->discriminatorValue,
|
||||
'discriminatorValues' => $this->getDiscriminatorValues($classMetadata),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -454,6 +454,23 @@ abstract class AbstractHydrator
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getDiscriminatorValues(ClassMetadata $classMetadata) : array
|
||||
{
|
||||
$values = array_map(
|
||||
function (string $subClass) : string {
|
||||
return (string) $this->getClassMetadata($subClass)->discriminatorValue;
|
||||
},
|
||||
$classMetadata->subClasses
|
||||
);
|
||||
|
||||
$values[] = (string) $classMetadata->discriminatorValue;
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve ClassMetadata associated to entity class name.
|
||||
*
|
||||
|
||||
@@ -419,7 +419,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) {
|
||||
$params[] = isset($sourceClass->fieldNames[$refColumnName])
|
||||
? $identifier[$sourceClass->fieldNames[$refColumnName]]
|
||||
: $identifier[$sourceClass->getFieldForColumn($columnName)];
|
||||
: $identifier[$sourceClass->getFieldForColumn($refColumnName)];
|
||||
}
|
||||
|
||||
return $params;
|
||||
|
||||
@@ -37,6 +37,8 @@ use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Doctrine\ORM\Utility\IdentifierFlattener;
|
||||
use Doctrine\ORM\Utility\PersisterHelper;
|
||||
use function array_merge;
|
||||
use function reset;
|
||||
|
||||
/**
|
||||
* A BasicEntityPersister maps an entity to a single table in a relational database.
|
||||
@@ -451,23 +453,21 @@ class BasicEntityPersister implements EntityPersister
|
||||
continue;
|
||||
}
|
||||
|
||||
$params[] = $identifier[$idField];
|
||||
$where[] = $this->class->associationMappings[$idField]['joinColumns'][0]['name'];
|
||||
$targetMapping = $this->em->getClassMetadata($this->class->associationMappings[$idField]['targetEntity']);
|
||||
$params[] = $identifier[$idField];
|
||||
$where[] = $this->quoteStrategy->getJoinColumnName(
|
||||
$this->class->associationMappings[$idField]['joinColumns'][0],
|
||||
$this->class,
|
||||
$this->platform
|
||||
);
|
||||
|
||||
switch (true) {
|
||||
case (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])):
|
||||
$types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
|
||||
break;
|
||||
$targetMapping = $this->em->getClassMetadata($this->class->associationMappings[$idField]['targetEntity']);
|
||||
$targetType = PersisterHelper::getTypeOfField($targetMapping->identifier[0], $targetMapping, $this->em);
|
||||
|
||||
case (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])):
|
||||
$types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ORMException::unrecognizedField($targetMapping->identifier[0]);
|
||||
if ($targetType === []) {
|
||||
throw ORMException::unrecognizedField($targetMapping->identifier[0]);
|
||||
}
|
||||
|
||||
$types[] = reset($targetType);
|
||||
}
|
||||
|
||||
if ($versioned) {
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\AST\Functions;
|
||||
use function strpos;
|
||||
|
||||
/**
|
||||
* An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language.
|
||||
@@ -303,21 +304,24 @@ class Parser
|
||||
$lookaheadType = $this->lexer->lookahead['type'];
|
||||
|
||||
// Short-circuit on first condition, usually types match
|
||||
if ($lookaheadType !== $token) {
|
||||
// If parameter is not identifier (1-99) must be exact match
|
||||
if ($token < Lexer::T_IDENTIFIER) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
}
|
||||
if ($lookaheadType === $token) {
|
||||
$this->lexer->moveNext();
|
||||
return;
|
||||
}
|
||||
|
||||
// If parameter is keyword (200+) must be exact match
|
||||
if ($token > Lexer::T_IDENTIFIER) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
}
|
||||
// If parameter is not identifier (1-99) must be exact match
|
||||
if ($token < Lexer::T_IDENTIFIER) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
}
|
||||
|
||||
// If parameter is T_IDENTIFIER, then matches T_IDENTIFIER (100) and keywords (200+)
|
||||
if ($token === Lexer::T_IDENTIFIER && $lookaheadType < Lexer::T_IDENTIFIER) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
}
|
||||
// If parameter is keyword (200+) must be exact match
|
||||
if ($token > Lexer::T_IDENTIFIER) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
}
|
||||
|
||||
// If parameter is T_IDENTIFIER, then matches T_IDENTIFIER (100) and keywords (200+)
|
||||
if ($token === Lexer::T_IDENTIFIER && $lookaheadType < Lexer::T_IDENTIFIER) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
}
|
||||
|
||||
$this->lexer->moveNext();
|
||||
@@ -958,20 +962,20 @@ class Parser
|
||||
if ($this->lexer->isNextToken(Lexer::T_FULLY_QUALIFIED_NAME)) {
|
||||
$this->match(Lexer::T_FULLY_QUALIFIED_NAME);
|
||||
|
||||
$schemaName = $this->lexer->token['value'];
|
||||
} else if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
|
||||
$this->match(Lexer::T_IDENTIFIER);
|
||||
|
||||
$schemaName = $this->lexer->token['value'];
|
||||
} else {
|
||||
$this->match(Lexer::T_ALIASED_NAME);
|
||||
|
||||
list($namespaceAlias, $simpleClassName) = explode(':', $this->lexer->token['value']);
|
||||
|
||||
$schemaName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
return $this->lexer->token['value'];
|
||||
}
|
||||
|
||||
return $schemaName;
|
||||
if ($this->lexer->isNextToken(Lexer::T_IDENTIFIER)) {
|
||||
$this->match(Lexer::T_IDENTIFIER);
|
||||
|
||||
return $this->lexer->token['value'];
|
||||
}
|
||||
|
||||
$this->match(Lexer::T_ALIASED_NAME);
|
||||
|
||||
[$namespaceAlias, $simpleClassName] = explode(':', $this->lexer->token['value']);
|
||||
|
||||
return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1280,7 +1284,9 @@ class Parser
|
||||
$this->match(Lexer::T_AS);
|
||||
}
|
||||
|
||||
$aliasIdentificationVariable = $this->AliasIdentificationVariable();
|
||||
$aliasIdentificationVariable = $this->lexer->isNextToken(Lexer::T_IDENTIFIER)
|
||||
? $this->AliasIdentificationVariable()
|
||||
: 'alias_should_have_been_set';
|
||||
|
||||
$deleteClause->aliasIdentificationVariable = $aliasIdentificationVariable;
|
||||
$class = $this->em->getClassMetadata($deleteClause->abstractSchemaName);
|
||||
|
||||
@@ -181,9 +181,9 @@ class SchemaValidator
|
||||
|
||||
$identifierColumns = $targetMetadata->getIdentifierColumnNames();
|
||||
foreach ($assoc['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) {
|
||||
if (!in_array($inverseJoinColumn['referencedColumnName'], $identifierColumns)) {
|
||||
$ce[] = "The referenced column name '" . $joinColumn['referencedColumnName'] . "' " .
|
||||
"has to be a primary key column on the target entity class '".$targetMetadata->name."'.";
|
||||
if (! in_array($inverseJoinColumn['referencedColumnName'], $identifierColumns)) {
|
||||
$ce[] = "The referenced column name '" . $inverseJoinColumn['referencedColumnName'] . "' " .
|
||||
"has to be a primary key column on the target entity class '" .$targetMetadata->name . "'.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,11 +171,11 @@ class Setup
|
||||
|
||||
|
||||
if (extension_loaded('memcached')) {
|
||||
$memcache = new \Memcached();
|
||||
$memcache->addServer('127.0.0.1', 11211);
|
||||
$memcached = new \Memcached();
|
||||
$memcached->addServer('127.0.0.1', 11211);
|
||||
|
||||
$cache = new \Doctrine\Common\Cache\MemcachedCache();
|
||||
$cache->setMemcache($memcache);
|
||||
$cache->setMemcached($memcached);
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
@@ -940,7 +940,11 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$class->setIdentifierValues($entity, $idValue);
|
||||
}
|
||||
|
||||
$this->entityIdentifiers[$oid] = $idValue;
|
||||
// Some identifiers may be foreign keys to new entities.
|
||||
// In this case, we don't have the value yet and should treat it as if we have a post-insert generator
|
||||
if (! $this->hasMissingIdsWhichAreForeignKeys($class, $idValue)) {
|
||||
$this->entityIdentifiers[$oid] = $idValue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->entityStates[$oid] = self::STATE_MANAGED;
|
||||
@@ -948,6 +952,20 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$this->scheduleForInsert($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $idValue
|
||||
*/
|
||||
private function hasMissingIdsWhichAreForeignKeys(ClassMetadata $class, array $idValue) : bool
|
||||
{
|
||||
foreach ($idValue as $idField => $idFieldValue) {
|
||||
if ($idFieldValue === null && isset($class->associationMappings[$idField])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL:
|
||||
* Computes the changeset of an individual entity, independently of the
|
||||
@@ -1033,12 +1051,16 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$persister = $this->getEntityPersister($className);
|
||||
$invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::postPersist);
|
||||
|
||||
$insertionsForClass = [];
|
||||
|
||||
foreach ($this->entityInsertions as $oid => $entity) {
|
||||
|
||||
if ($this->em->getClassMetadata(get_class($entity))->name !== $className) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$insertionsForClass[$oid] = $entity;
|
||||
|
||||
$persister->addInsert($entity);
|
||||
|
||||
unset($this->entityInsertions[$oid]);
|
||||
@@ -1067,6 +1089,14 @@ class UnitOfWork implements PropertyChangedListener
|
||||
|
||||
$this->addToIdentityMap($entity);
|
||||
}
|
||||
} else {
|
||||
foreach ($insertionsForClass as $oid => $entity) {
|
||||
if (! isset($this->entityIdentifiers[$oid])) {
|
||||
//entity was not added to identity map because some identifiers are foreign keys to new entities.
|
||||
//add it now
|
||||
$this->addToEntityIdentifiersAndEntityMap($class, $oid, $entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
@@ -1074,6 +1104,30 @@ class UnitOfWork implements PropertyChangedListener
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $entity
|
||||
*/
|
||||
private function addToEntityIdentifiersAndEntityMap(ClassMetadata $class, string $oid, $entity): void
|
||||
{
|
||||
$identifier = [];
|
||||
|
||||
foreach ($class->getIdentifierFieldNames() as $idField) {
|
||||
$value = $class->getFieldValue($entity, $idField);
|
||||
|
||||
if (isset($class->associationMappings[$idField])) {
|
||||
// NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced.
|
||||
$value = $this->getSingleIdentifierValue($value);
|
||||
}
|
||||
|
||||
$identifier[$idField] = $this->originalEntityData[$oid][$idField] = $value;
|
||||
}
|
||||
|
||||
$this->entityStates[$oid] = self::STATE_MANAGED;
|
||||
$this->entityIdentifiers[$oid] = $identifier;
|
||||
|
||||
$this->addToIdentityMap($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all entity updates for entities of the specified type.
|
||||
*
|
||||
|
||||
@@ -35,7 +35,7 @@ class Version
|
||||
/**
|
||||
* Current Doctrine Version
|
||||
*/
|
||||
const VERSION = '2.6.0';
|
||||
const VERSION = '2.6.1-DEV';
|
||||
|
||||
/**
|
||||
* Compares a Doctrine version with the current one.
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\ManyToManyPersister;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="manytomanypersister_child")
|
||||
*/
|
||||
class ChildClass
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(name="id1", type="integer")
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $id1;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity=OtherParentClass::class, cascade={"persist"})
|
||||
* @JoinColumn(name="other_parent_id", referencedColumnName="id")
|
||||
*
|
||||
* @var OtherParentClass
|
||||
*/
|
||||
public $otherParent;
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity=ParentClass::class, inversedBy="children")
|
||||
* @JoinTable(
|
||||
* name="parent_child",
|
||||
* joinColumns={
|
||||
* @JoinColumn(name="child_id1", referencedColumnName="id1"),
|
||||
* @JoinColumn(name="child_id2", referencedColumnName="other_parent_id")
|
||||
* },
|
||||
* inverseJoinColumns={@JoinColumn(name="parent_id", referencedColumnName="id")}
|
||||
* )
|
||||
*
|
||||
* @var Collection|ParentClass[]
|
||||
*/
|
||||
public $parents;
|
||||
|
||||
public function __construct(int $id1, OtherParentClass $otherParent)
|
||||
{
|
||||
$this->id1 = $id1;
|
||||
$this->otherParent = $otherParent;
|
||||
$this->parents = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\ManyToManyPersister;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="manytomanypersister_other_parent")
|
||||
*/
|
||||
class OtherParentClass
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(name="id", type="integer")
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $id;
|
||||
|
||||
public function __construct(int $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\ManyToManyPersister;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Table;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Mapping\ManyToMany;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="manytomanypersister_parent")
|
||||
*/
|
||||
class ParentClass
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(name="id", type="integer")
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity=ChildClass::class, mappedBy="parents", orphanRemoval=true, cascade={"persist"})
|
||||
*
|
||||
* @var Collection|ChildClass[]
|
||||
*/
|
||||
public $children;
|
||||
|
||||
public function __construct(int $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->children = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace Doctrine\Tests\ORM\Functional;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\Tests\Models\Company\CompanyManager;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Functional Query tests.
|
||||
@@ -292,7 +293,7 @@ class QueryDqlFunctionTest extends OrmFunctionalTestCase
|
||||
*
|
||||
* @dataProvider dateAddSubProvider
|
||||
*/
|
||||
public function testDateAdd(string $unit, int $amount, int $expectedValue, int $delta = 0) : void
|
||||
public function testDateAdd(string $unit, int $amount, int $delta = 0) : void
|
||||
{
|
||||
$query = sprintf(
|
||||
'SELECT CURRENT_TIMESTAMP() as now, DATE_ADD(CURRENT_TIMESTAMP(), %d, \'%s\') AS add FROM %s m',
|
||||
@@ -308,9 +309,12 @@ class QueryDqlFunctionTest extends OrmFunctionalTestCase
|
||||
self::assertArrayHasKey('now', $result);
|
||||
self::assertArrayHasKey('add', $result);
|
||||
|
||||
$diff = strtotime($result['add']) - strtotime($result['now']);
|
||||
|
||||
self::assertEquals($expectedValue, $diff, '', $delta);
|
||||
self::assertEquals(
|
||||
(new \DateTimeImmutable($result['now']))->modify(sprintf('+%d %s', $amount, $unit)),
|
||||
new \DateTimeImmutable($result['add']),
|
||||
'',
|
||||
$delta
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,7 +323,7 @@ class QueryDqlFunctionTest extends OrmFunctionalTestCase
|
||||
*
|
||||
* @dataProvider dateAddSubProvider
|
||||
*/
|
||||
public function testDateSub(string $unit, int $amount, int $expectedValue, int $delta = 0) : void
|
||||
public function testDateSub(string $unit, int $amount, int $delta = 0) : void
|
||||
{
|
||||
$query = sprintf(
|
||||
'SELECT CURRENT_TIMESTAMP() as now, DATE_SUB(CURRENT_TIMESTAMP(), %d, \'%s\') AS sub FROM %s m',
|
||||
@@ -335,9 +339,12 @@ class QueryDqlFunctionTest extends OrmFunctionalTestCase
|
||||
self::assertArrayHasKey('now', $result);
|
||||
self::assertArrayHasKey('sub', $result);
|
||||
|
||||
$diff = strtotime($result['now']) - strtotime($result['sub']);
|
||||
|
||||
self::assertEquals($expectedValue, $diff, '', $delta);
|
||||
self::assertEquals(
|
||||
(new \DateTimeImmutable($result['now']))->modify(sprintf('-%d %s', $amount, $unit)),
|
||||
new \DateTimeImmutable($result['sub']),
|
||||
'',
|
||||
$delta
|
||||
);
|
||||
}
|
||||
|
||||
public function dateAddSubProvider() : array
|
||||
@@ -345,9 +352,10 @@ class QueryDqlFunctionTest extends OrmFunctionalTestCase
|
||||
$secondsInDay = 86400;
|
||||
|
||||
return [
|
||||
'year' => ['year', 1, 365 * $secondsInDay, 3 * $secondsInDay],
|
||||
'month' => ['month', 1, 30 * $secondsInDay, 2 * $secondsInDay],
|
||||
'week' => ['week', 1, 7 * $secondsInDay, $secondsInDay],
|
||||
'year' => ['year', 1, $secondsInDay],
|
||||
'month' => ['month', 1, $secondsInDay],
|
||||
'week' => ['week', 1, $secondsInDay],
|
||||
'day' => ['day', 2, $secondsInDay],
|
||||
'hour' => ['hour', 1, 3600],
|
||||
'minute' => ['minute', 1, 60],
|
||||
'second' => ['second', 10, 10],
|
||||
|
||||
190
tests/Doctrine/Tests/ORM/Functional/Ticket/GH6531Test.php
Normal file
190
tests/Doctrine/Tests/ORM/Functional/Ticket/GH6531Test.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
final class GH6531Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp() : void
|
||||
{
|
||||
parent::setup();
|
||||
|
||||
$this->setUpEntitySchema(
|
||||
[
|
||||
GH6531User::class,
|
||||
GH6531Address::class,
|
||||
GH6531Article::class,
|
||||
GH6531ArticleAttribute::class,
|
||||
GH6531Order::class,
|
||||
GH6531OrderItem::class,
|
||||
GH6531Product::class,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 6531
|
||||
*/
|
||||
public function testSimpleDerivedIdentity() : void
|
||||
{
|
||||
$user = new GH6531User();
|
||||
$address = new GH6531Address();
|
||||
$address->user = $user;
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->persist($address);
|
||||
$this->_em->flush();
|
||||
|
||||
self::assertSame($user, $this->_em->find(GH6531User::class, $user->id));
|
||||
self::assertSame($address, $this->_em->find(GH6531Address::class, $user));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 6531
|
||||
*/
|
||||
public function testDynamicAttributes() : void
|
||||
{
|
||||
$article = new GH6531Article();
|
||||
$article->addAttribute('name', 'value');
|
||||
|
||||
$this->_em->persist($article);
|
||||
$this->_em->flush();
|
||||
|
||||
self::assertSame(
|
||||
$article->attributes['name'],
|
||||
$this->_em->find(GH6531ArticleAttribute::class, ['article' => $article, 'attribute' => 'name'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 6531
|
||||
*/
|
||||
public function testJoinTableWithMetadata() : void
|
||||
{
|
||||
$product = new GH6531Product();
|
||||
$this->_em->persist($product);
|
||||
$this->_em->flush();
|
||||
|
||||
$order = new GH6531Order();
|
||||
$order->addItem($product, 2);
|
||||
|
||||
$this->_em->persist($order);
|
||||
$this->_em->flush();
|
||||
|
||||
self::assertSame(
|
||||
$order->items->first(),
|
||||
$this->_em->find(GH6531OrderItem::class, ['product' => $product, 'order' => $order])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH6531User
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH6531Address
|
||||
{
|
||||
/** @Id @OneToOne(targetEntity=GH6531User::class) */
|
||||
public $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH6531Article
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
|
||||
/** @OneToMany(targetEntity=GH6531ArticleAttribute::class, mappedBy="article", cascade={"ALL"}, indexBy="attribute") */
|
||||
public $attributes;
|
||||
|
||||
public function addAttribute(string $name, string $value)
|
||||
{
|
||||
$this->attributes[$name] = new GH6531ArticleAttribute($name, $value, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH6531ArticleAttribute
|
||||
{
|
||||
/** @Id @ManyToOne(targetEntity=GH6531Article::class, inversedBy="attributes") */
|
||||
public $article;
|
||||
|
||||
/** @Id @Column(type="string") */
|
||||
public $attribute;
|
||||
|
||||
/** @Column(type="string") */
|
||||
public $value;
|
||||
|
||||
public function __construct(string $name, string $value, GH6531Article $article)
|
||||
{
|
||||
$this->attribute = $name;
|
||||
$this->value = $value;
|
||||
$this->article = $article;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH6531Order
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
|
||||
/** @OneToMany(targetEntity=GH6531OrderItem::class, mappedBy="order", cascade={"ALL"}) */
|
||||
public $items;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->items = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function addItem(GH6531Product $product, int $amount) : void
|
||||
{
|
||||
$this->items->add(new GH6531OrderItem($this, $product, $amount));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH6531Product
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH6531OrderItem
|
||||
{
|
||||
/** @Id @ManyToOne(targetEntity=GH6531Order::class) */
|
||||
public $order;
|
||||
|
||||
/** @Id @ManyToOne(targetEntity=GH6531Product::class) */
|
||||
public $product;
|
||||
|
||||
/** @Column(type="integer") */
|
||||
public $amount = 1;
|
||||
|
||||
public function __construct(GH6531Order $order, GH6531Product $product, int $amount = 1)
|
||||
{
|
||||
$this->order = $order;
|
||||
$this->product = $product;
|
||||
$this->amount = $amount;
|
||||
}
|
||||
}
|
||||
119
tests/Doctrine/Tests/ORM/Functional/Ticket/GH6937Test.php
Normal file
119
tests/Doctrine/Tests/ORM/Functional/Ticket/GH6937Test.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
/**
|
||||
* @group 6937
|
||||
*/
|
||||
final class GH6937Test extends OrmFunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function setUp() : void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpEntitySchema([GH6937Person::class, GH6937Employee::class, GH6937Manager::class]);
|
||||
}
|
||||
|
||||
public function testPhoneNumberIsPopulatedWithFind() : void
|
||||
{
|
||||
$manager = new GH6937Manager();
|
||||
$manager->name = 'Kevin';
|
||||
$manager->phoneNumber = '555-5555';
|
||||
$manager->department = 'Accounting';
|
||||
|
||||
$this->_em->persist($manager);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$persistedManager = $this->_em->find(GH6937Person::class, $manager->id);
|
||||
|
||||
self::assertSame('Kevin', $persistedManager->name);
|
||||
self::assertSame('555-5555', $persistedManager->phoneNumber);
|
||||
self::assertSame('Accounting', $persistedManager->department);
|
||||
}
|
||||
|
||||
public function testPhoneNumberIsPopulatedWithQueryBuilderUsingSimpleObjectHydrator() : void
|
||||
{
|
||||
$manager = new GH6937Manager();
|
||||
$manager->name = 'Kevin';
|
||||
$manager->phoneNumber = '555-5555';
|
||||
$manager->department = 'Accounting';
|
||||
|
||||
$this->_em->persist($manager);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$persistedManager = $this->_em->getRepository(GH6937Person::class)
|
||||
->createQueryBuilder('e')
|
||||
->where('e.id = :id')
|
||||
->setParameter('id', $manager->id)
|
||||
->getQuery()
|
||||
->getOneOrNullResult(AbstractQuery::HYDRATE_SIMPLEOBJECT);
|
||||
|
||||
self::assertSame('Kevin', $persistedManager->name);
|
||||
self::assertSame('555-5555', $persistedManager->phoneNumber);
|
||||
self::assertSame('Accounting', $persistedManager->department);
|
||||
}
|
||||
|
||||
public function testPhoneNumberIsPopulatedWithQueryBuilder() : void
|
||||
{
|
||||
$manager = new GH6937Manager();
|
||||
$manager->name = 'Kevin';
|
||||
$manager->phoneNumber = '555-5555';
|
||||
$manager->department = 'Accounting';
|
||||
|
||||
$this->_em->persist($manager);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$persistedManager = $this->_em->getRepository(GH6937Person::class)
|
||||
->createQueryBuilder('e')
|
||||
->where('e.id = :id')
|
||||
->setParameter('id', $manager->id)
|
||||
->getQuery()
|
||||
->getOneOrNullResult();
|
||||
|
||||
self::assertSame('Kevin', $persistedManager->name);
|
||||
self::assertSame('555-5555', $persistedManager->phoneNumber);
|
||||
self::assertSame('Accounting', $persistedManager->department);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @InheritanceType("JOINED")
|
||||
* @DiscriminatorColumn(name="discr", type="string")
|
||||
* @DiscriminatorMap({"employee"=GH6937Employee::class, "manager"=GH6937Manager::class})
|
||||
*/
|
||||
abstract class GH6937Person
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
|
||||
/** @Column(type="string") */
|
||||
public $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
abstract class GH6937Employee extends GH6937Person
|
||||
{
|
||||
/** @Column(type="string") */
|
||||
public $phoneNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH6937Manager extends GH6937Employee
|
||||
{
|
||||
/** @Column(type="string") */
|
||||
public $department;
|
||||
}
|
||||
82
tests/Doctrine/Tests/ORM/Functional/Ticket/GH7012Test.php
Normal file
82
tests/Doctrine/Tests/ORM/Functional/Ticket/GH7012Test.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Tests\Models\Quote\User as QuotedUser;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
final class GH7012Test extends OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp() : void
|
||||
{
|
||||
$this->useModelSet('quote');
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpEntitySchema([GH7012UserData::class]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 7012
|
||||
*/
|
||||
public function testUpdateEntityWithIdentifierAssociationWithQuotedJoinColumn() : void
|
||||
{
|
||||
$user = new QuotedUser();
|
||||
$user->name = 'John Doe';
|
||||
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
|
||||
$userData = new GH7012UserData($user, '123456789');
|
||||
|
||||
$this->_em->persist($userData);
|
||||
$this->_em->flush();
|
||||
|
||||
$userData->name = '4321';
|
||||
$this->_em->flush();
|
||||
|
||||
$platform = $this->_em->getConnection()->getDatabasePlatform();
|
||||
$quotedTableName = $platform->quoteIdentifier('quote-user-data');
|
||||
$quotedColumn = $platform->quoteIdentifier('name');
|
||||
$quotedIdentifier = $platform->quoteIdentifier('user-id');
|
||||
|
||||
self::assertNotEquals('quote-user-data', $quotedTableName);
|
||||
self::assertNotEquals('name', $quotedColumn);
|
||||
self::assertNotEquals('user-id', $quotedIdentifier);
|
||||
|
||||
$queries = $this->_sqlLoggerStack->queries;
|
||||
|
||||
$this->assertSQLEquals(
|
||||
sprintf('UPDATE %s SET %s = ? WHERE %s = ?', $quotedTableName, $quotedColumn, $quotedIdentifier),
|
||||
$queries[$this->_sqlLoggerStack->currentQuery - 1]['sql']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="`quote-user-data`")
|
||||
*/
|
||||
class GH7012UserData
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @OneToOne(targetEntity=Doctrine\Tests\Models\Quote\User::class)
|
||||
* @JoinColumn(name="`user-id`", referencedColumnName="`user-id`", onDelete="CASCADE")
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* @Column(type="string", name="`name`")
|
||||
*/
|
||||
public $name;
|
||||
|
||||
public function __construct(QuotedUser $user, string $name)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
||||
212
tests/Doctrine/Tests/ORM/Functional/Ticket/GH7062Test.php
Normal file
212
tests/Doctrine/Tests/ORM/Functional/Ticket/GH7062Test.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
class GH7062Test extends OrmFunctionalTestCase
|
||||
{
|
||||
private const SEASON_ID = 'season_18';
|
||||
private const TEAM_ID = 'team_A';
|
||||
|
||||
protected function setUp() : void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpEntitySchema(
|
||||
[
|
||||
GH7062Team::class,
|
||||
GH7062Season::class,
|
||||
GH7062Ranking::class,
|
||||
GH7062RankingPosition::class
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 7062
|
||||
*/
|
||||
public function testEntityWithAssociationKeyIdentityCanBeUpdated() : void
|
||||
{
|
||||
$this->createInitialRankingWithRelatedEntities();
|
||||
$this->modifyRanking();
|
||||
$this->verifyRanking();
|
||||
}
|
||||
|
||||
private function createInitialRankingWithRelatedEntities() : void
|
||||
{
|
||||
$team = new GH7062Team(self::TEAM_ID);
|
||||
$season = new GH7062Season(self::SEASON_ID);
|
||||
|
||||
$season->ranking = new GH7062Ranking($season, [$team]);
|
||||
|
||||
$this->_em->persist($team);
|
||||
$this->_em->persist($season);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
foreach ($season->ranking->positions as $position) {
|
||||
self::assertSame(0, $position->points);
|
||||
}
|
||||
}
|
||||
|
||||
private function modifyRanking() : void
|
||||
{
|
||||
/** @var GH7062Ranking $ranking */
|
||||
$ranking = $this->_em->find(GH7062Ranking::class, self::SEASON_ID);
|
||||
|
||||
foreach ($ranking->positions as $position) {
|
||||
$position->points += 3;
|
||||
}
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
}
|
||||
|
||||
private function verifyRanking() : void
|
||||
{
|
||||
/** @var GH7062Season $season */
|
||||
$season = $this->_em->find(GH7062Season::class, self::SEASON_ID);
|
||||
self::assertInstanceOf(GH7062Season::class, $season);
|
||||
|
||||
$ranking = $season->ranking;
|
||||
self::assertInstanceOf(GH7062Ranking::class, $ranking);
|
||||
|
||||
foreach ($ranking->positions as $position) {
|
||||
self::assertSame(3, $position->points);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple Entity whose identity is defined through another Entity (Season)
|
||||
*
|
||||
* @Entity
|
||||
* @Table(name="soccer_rankings")
|
||||
*/
|
||||
class GH7062Ranking
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @OneToOne(targetEntity=GH7062Season::class, inversedBy="ranking")
|
||||
* @JoinColumn(name="season", referencedColumnName="id")
|
||||
*
|
||||
* @var GH7062Season
|
||||
*/
|
||||
public $season;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity=GH7062RankingPosition::class, mappedBy="ranking", cascade={"all"})
|
||||
*
|
||||
* @var Collection|GH7062RankingPosition[]
|
||||
*/
|
||||
public $positions;
|
||||
|
||||
/**
|
||||
* @param GH7062Team[] $teams
|
||||
*/
|
||||
public function __construct(GH7062Season $season, array $teams)
|
||||
{
|
||||
$this->season = $season;
|
||||
$this->positions = new ArrayCollection();
|
||||
|
||||
foreach ($teams as $team) {
|
||||
$this->positions[] = new GH7062RankingPosition($this, $team);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity which serves as a identity provider for other entities
|
||||
*
|
||||
* @Entity
|
||||
* @Table(name="soccer_seasons")
|
||||
*/
|
||||
class GH7062Season
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="string")
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @OneToOne(targetEntity=GH7062Ranking::class, mappedBy="season", cascade={"all"})
|
||||
*
|
||||
* @var GH7062Ranking|null
|
||||
*/
|
||||
public $ranking;
|
||||
|
||||
public function __construct(string $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity which serves as a identity provider for other entities
|
||||
*
|
||||
* @Entity
|
||||
* @Table(name="soccer_teams")
|
||||
*/
|
||||
class GH7062Team
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="string")
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
public function __construct(string $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity whose identity is defined through two other entities
|
||||
*
|
||||
* @Entity
|
||||
* @Table(name="soccer_ranking_positions")
|
||||
*/
|
||||
class GH7062RankingPosition
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity=GH7062Ranking::class, inversedBy="positions")
|
||||
* @JoinColumn(name="season", referencedColumnName="season")
|
||||
*
|
||||
* @var GH7062Ranking
|
||||
*/
|
||||
public $ranking;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
* @ManyToOne(targetEntity=GH7062Team::class)
|
||||
* @JoinColumn(name="team_id", referencedColumnName="id")
|
||||
*
|
||||
* @var GH7062Team
|
||||
*/
|
||||
public $team;
|
||||
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $points;
|
||||
|
||||
public function __construct(GH7062Ranking $ranking, GH7062Team $team)
|
||||
{
|
||||
$this->ranking = $ranking;
|
||||
$this->team = $team;
|
||||
$this->points = 0;
|
||||
}
|
||||
}
|
||||
69
tests/Doctrine/Tests/ORM/Functional/Ticket/GH7067Test.php
Normal file
69
tests/Doctrine/Tests/ORM/Functional/Ticket/GH7067Test.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
final class GH7067Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
public function setUp() : void
|
||||
{
|
||||
$this->enableSecondLevelCache();
|
||||
parent::setUp();
|
||||
|
||||
$this->setUpEntitySchema([GH7067Entity::class]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 7067
|
||||
*/
|
||||
public function testSLCWithVersion() : void
|
||||
{
|
||||
$entity = new GH7067Entity();
|
||||
$entity->lastUpdate = new \DateTime();
|
||||
|
||||
$this->_em->persist($entity);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
/** @var GH7067Entity $notCached */
|
||||
$notCached = $this->_em->find(GH7067Entity::class, $entity->id);
|
||||
|
||||
self::assertNotNull($notCached->version, 'Version already cached by persister above, it must be not null');
|
||||
|
||||
$notCached->lastUpdate = new \DateTime('+1 seconds');
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity()
|
||||
* @Cache(usage="NONSTRICT_READ_WRITE")
|
||||
*/
|
||||
class GH7067Entity
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @Column(type="datetime")
|
||||
*
|
||||
* @var \DateTime
|
||||
*/
|
||||
public $lastUpdate;
|
||||
|
||||
/**
|
||||
* @Column(type="datetime")
|
||||
* @Version
|
||||
*
|
||||
* @var \DateTime
|
||||
*/
|
||||
public $version;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Persisters;
|
||||
|
||||
use Doctrine\ORM\Persisters\Collection\ManyToManyPersister;
|
||||
use Doctrine\Tests\Mocks\ConnectionMock;
|
||||
use Doctrine\Tests\Models\ManyToManyPersister\ChildClass;
|
||||
use Doctrine\Tests\Models\ManyToManyPersister\OtherParentClass;
|
||||
use Doctrine\Tests\Models\ManyToManyPersister\ParentClass;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
|
||||
/**
|
||||
* @covers \Doctrine\ORM\Persisters\Collection\ManyToManyPersister
|
||||
*/
|
||||
final class ManyToManyPersisterTest extends OrmTestCase
|
||||
{
|
||||
/**
|
||||
* @group 6991
|
||||
* @group ManyToManyPersister
|
||||
*
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
*/
|
||||
public function testDeleteManyToManyCollection(): void
|
||||
{
|
||||
$parent = new ParentClass(1);
|
||||
$otherParent = new OtherParentClass(42);
|
||||
$child = new ChildClass(1, $otherParent);
|
||||
|
||||
$parent->children->add($child);
|
||||
$child->parents->add($parent);
|
||||
|
||||
$em = $this->_getTestEntityManager();
|
||||
$em->persist($parent);
|
||||
$em->flush();
|
||||
|
||||
/** @var ChildClass|null $childReloaded */
|
||||
$childReloaded = $em->find(ChildClass::class, ['id1' => 1, 'otherParent' => $otherParent]);
|
||||
|
||||
self::assertNotNull($childReloaded);
|
||||
|
||||
$persister = new ManyToManyPersister($em);
|
||||
$persister->delete($childReloaded->parents);
|
||||
|
||||
/** @var ConnectionMock $conn */
|
||||
$conn = $em->getConnection();
|
||||
|
||||
$updates = $conn->getExecuteUpdates();
|
||||
$lastUpdate = array_pop($updates);
|
||||
|
||||
self::assertEquals('DELETE FROM parent_child WHERE child_id1 = ? AND child_id2 = ?', $lastUpdate['query']);
|
||||
self::assertEquals([1, 42], $lastUpdate['params']);
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,17 @@ class DeleteSqlGenerationTest extends OrmTestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group 6939
|
||||
*/
|
||||
public function testSupportsDeleteWithoutWhereAndAlias() : void
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'DELETE FROM Doctrine\Tests\Models\CMS\CmsUser',
|
||||
'DELETE FROM cms_users'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupportsDeleteWithoutWhereAndFrom()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
|
||||
Reference in New Issue
Block a user