-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Correct DQL INSTANCE OF
to filter all possible child classes
#6392
Merged
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
e798bfe
[QUERY] "INSTANCE OF" now behaves correctly with subclasses
taueres 4eb4465
Fix as per review
Jean85 3219fe5
Fix small CS issues as per review
Jean85 11c84c7
Split SqlWalker::walkInstanceOfExpression method
Jean85 21e12ef
Move tests to ticket namespace (and rename them)
Jean85 96bcee4
Fix test
Jean85 30256e7
Refactor a bit the SqlWalker modifications
Jean85 11c54b7
Apply additional fixes to the SqlWalker to fix tests
Jean85 8b9c297
Put all tests classes in a single namespace
Jean85 ba69cc8
Simplify stubs used in tests
Jean85 d4cdc6e
Adding a failing test case for parameter binding using metadata with …
Jean85 7d98135
[QUERY] "INSTANCE OF" now behaves correctly with subclasses
taueres 04acde6
Fix as per review
Jean85 aa233f8
Fix small CS issues as per review
Jean85 0e88f1b
Split SqlWalker::walkInstanceOfExpression method
Jean85 bd47ec9
Move tests to ticket namespace (and rename them)
Jean85 31d2d84
Fix test
Jean85 5181eae
Refactor a bit the SqlWalker modifications
Jean85 167dfde
Apply additional fixes to the SqlWalker to fix tests
Jean85 d2f7514
Put all tests classes in a single namespace
Jean85 2fd8752
Simplify stubs used in tests
Jean85 b1f7c59
Adding a failing test case for parameter binding using metadata with …
Jean85 e91dcf8
Fix discriminator resolution when using parameters
c7ef908
Merge additional fix (and master changes) from taueres/fix-instance-o…
Jean85 d4db126
Remove code duplication of the getAllDiscriminators method
Jean85 5224a89
Apply various and CS fixes as per review
Jean85 9864a5a
Add unit test for HierarchyDiscriminatorResolverTest
Jean85 19bc499
Add more CS fixes
Jean85 c799c6d
Add new functional test to check usage of INSTANCEOF with multiple pa…
Jean85 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
lib/Doctrine/ORM/Utility/HierarchyDiscriminatorResolver.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
|
||
namespace Doctrine\ORM\Utility; | ||
|
||
use Doctrine\Common\Persistence\Mapping\ClassMetadata; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
|
||
/** | ||
* @internal This class exists only to avoid code duplication, do not reuse it externally | ||
*/ | ||
final class HierarchyDiscriminatorResolver | ||
{ | ||
private function __construct() | ||
{ | ||
} | ||
|
||
/** | ||
* This method is needed to make INSTANCEOF work correctly with inheritance: if the class at hand has inheritance, | ||
* it extracts all the discriminators from the child classes and returns them | ||
*/ | ||
public static function resolveDiscriminatorsForClass( | ||
ClassMetadata $rootClassMetadata, | ||
EntityManagerInterface $entityManager | ||
): array { | ||
$hierarchyClasses = $rootClassMetadata->subClasses; | ||
$hierarchyClasses[] = $rootClassMetadata->name; | ||
|
||
$discriminators = []; | ||
|
||
foreach ($hierarchyClasses as $class) { | ||
$currentMetadata = $entityManager->getClassMetadata($class); | ||
$currentDiscriminator = $currentMetadata->discriminatorValue; | ||
|
||
if (null !== $currentDiscriminator) { | ||
$discriminators[$currentDiscriminator] = null; | ||
} | ||
} | ||
|
||
return $discriminators; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
tests/Doctrine/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfAbstractTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?php | ||
|
||
namespace Doctrine\Tests\ORM\Functional\Ticket; | ||
|
||
use Doctrine\Tests\OrmFunctionalTestCase; | ||
|
||
class Ticket4646InstanceOfAbstractTest extends OrmFunctionalTestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
$this->_schemaTool->createSchema([ | ||
$this->_em->getClassMetadata(PersonTicket4646Abstract::class), | ||
$this->_em->getClassMetadata(EmployeeTicket4646Abstract::class), | ||
]); | ||
} | ||
|
||
public function testInstanceOf(): void | ||
{ | ||
$this->_em->persist(new EmployeeTicket4646Abstract()); | ||
$this->_em->flush(); | ||
|
||
$dql = 'SELECT p FROM Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646Abstract p | ||
WHERE p INSTANCE OF Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646Abstract'; | ||
$query = $this->_em->createQuery($dql); | ||
$result = $query->getResult(); | ||
|
||
self::assertCount(1, $result); | ||
self::assertContainsOnlyInstancesOf(PersonTicket4646Abstract::class, $result); | ||
} | ||
} | ||
|
||
/** | ||
* @Entity() | ||
* @Table(name="instance_of_abstract_test_person") | ||
* @InheritanceType(value="JOINED") | ||
* @DiscriminatorColumn(name="kind", type="string") | ||
* @DiscriminatorMap(value={ | ||
* "employee": EmployeeTicket4646Abstract::class | ||
* }) | ||
*/ | ||
abstract class PersonTicket4646Abstract | ||
{ | ||
/** | ||
* @Id() | ||
* @GeneratedValue() | ||
* @Column(type="integer") | ||
*/ | ||
private $id; | ||
|
||
public function getId(): ?int | ||
{ | ||
return $this->id; | ||
} | ||
} | ||
|
||
/** | ||
* @Entity() | ||
* @Table(name="instance_of_abstract_test_employee") | ||
*/ | ||
class EmployeeTicket4646Abstract extends PersonTicket4646Abstract | ||
{ | ||
} |
77 changes: 77 additions & 0 deletions
77
tests/Doctrine/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfMultiLevelTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<?php | ||
|
||
namespace Doctrine\Tests\ORM\Functional\Ticket; | ||
|
||
use Doctrine\Tests\OrmFunctionalTestCase; | ||
|
||
class Ticket4646InstanceOfMultiLevelTest extends OrmFunctionalTestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
$this->_schemaTool->createSchema([ | ||
$this->_em->getClassMetadata(PersonTicket4646MultiLevel::class), | ||
$this->_em->getClassMetadata(EmployeeTicket4646MultiLevel::class), | ||
$this->_em->getClassMetadata(EngineerTicket4646MultiLevel::class), | ||
]); | ||
} | ||
|
||
public function testInstanceOf(): void | ||
{ | ||
$this->_em->persist(new PersonTicket4646MultiLevel()); | ||
$this->_em->persist(new EmployeeTicket4646MultiLevel()); | ||
$this->_em->persist(new EngineerTicket4646MultiLevel()); | ||
$this->_em->flush(); | ||
|
||
$dql = 'SELECT p FROM Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646MultiLevel p | ||
WHERE p INSTANCE OF Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646MultiLevel'; | ||
$query = $this->_em->createQuery($dql); | ||
$result = $query->getResult(); | ||
|
||
self::assertCount(3, $result); | ||
self::assertContainsOnlyInstancesOf(PersonTicket4646MultiLevel::class, $result); | ||
} | ||
} | ||
|
||
/** | ||
* @Entity() | ||
* @Table(name="instance_of_multi_level_test_person") | ||
* @InheritanceType(value="JOINED") | ||
* @DiscriminatorColumn(name="kind", type="string") | ||
* @DiscriminatorMap(value={ | ||
* "person": "Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646MultiLevel", | ||
* "employee": "Doctrine\Tests\ORM\Functional\Ticket\EmployeeTicket4646MultiLevel", | ||
* "engineer": "Doctrine\Tests\ORM\Functional\Ticket\EngineerTicket4646MultiLevel", | ||
* }) | ||
*/ | ||
class PersonTicket4646MultiLevel | ||
{ | ||
/** | ||
* @Id() | ||
* @GeneratedValue() | ||
* @Column(type="integer") | ||
*/ | ||
private $id; | ||
|
||
public function getId(): ?int | ||
{ | ||
return $this->id; | ||
} | ||
} | ||
|
||
/** | ||
* @Entity() | ||
* @Table(name="instance_of_multi_level_employee") | ||
*/ | ||
class EmployeeTicket4646MultiLevel extends PersonTicket4646MultiLevel | ||
{ | ||
} | ||
|
||
/** | ||
* @Entity() | ||
* @Table(name="instance_of_multi_level_engineer") | ||
*/ | ||
class EngineerTicket4646MultiLevel extends EmployeeTicket4646MultiLevel | ||
{ | ||
} |
67 changes: 67 additions & 0 deletions
67
tests/Doctrine/Tests/ORM/Functional/Ticket/Ticket4646InstanceOfParametricTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?php | ||
|
||
namespace Doctrine\Tests\ORM\Functional\Ticket; | ||
|
||
use Doctrine\Tests\OrmFunctionalTestCase; | ||
|
||
class Ticket4646InstanceOfParametricTest extends OrmFunctionalTestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
$this->_schemaTool->createSchema([ | ||
$this->_em->getClassMetadata(PersonTicket4646Parametric::class), | ||
$this->_em->getClassMetadata(EmployeeTicket4646Parametric::class), | ||
]); | ||
} | ||
|
||
public function testInstanceOf(): void | ||
{ | ||
$this->_em->persist(new PersonTicket4646Parametric()); | ||
$this->_em->persist(new EmployeeTicket4646Parametric()); | ||
$this->_em->flush(); | ||
$dql = 'SELECT p FROM Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646Parametric p | ||
WHERE p INSTANCE OF :parameter'; | ||
$query = $this->_em->createQuery($dql); | ||
$query->setParameter( | ||
'parameter', | ||
$this->_em->getClassMetadata(PersonTicket4646Parametric::class) | ||
); | ||
$result = $query->getResult(); | ||
self::assertCount(2, $result); | ||
self::assertContainsOnlyInstancesOf(PersonTicket4646Parametric::class, $result); | ||
} | ||
} | ||
|
||
/** | ||
* @Entity() | ||
* @Table(name="instance_of_parametric_person") | ||
* @InheritanceType(value="JOINED") | ||
* @DiscriminatorColumn(name="kind", type="string") | ||
* @DiscriminatorMap(value={ | ||
* "person": "Doctrine\Tests\ORM\Functional\Ticket\PersonTicket4646Parametric", | ||
* "employee": "Doctrine\Tests\ORM\Functional\Ticket\EmployeeTicket4646Parametric" | ||
* }) | ||
*/ | ||
class PersonTicket4646Parametric | ||
{ | ||
/** | ||
* @Id() | ||
* @GeneratedValue() | ||
* @Column(type="integer") | ||
*/ | ||
private $id; | ||
|
||
public function getId(): ?int | ||
{ | ||
return $this->id; | ||
} | ||
} | ||
|
||
/** | ||
* @Entity() | ||
* @Table(name="instance_of_parametric_employee") | ||
*/ | ||
class EmployeeTicket4646Parametric extends PersonTicket4646Parametric | ||
{ | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to this logic, the list may be empty, leading to a crash
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How that could happen? Since I check if the class is unrelated at line 2297, at least one discriminator should pop up, or an exception be thrown...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but that flow makes it hard to follow: ideally we should
array_map()
over$instanceOfExpr->value
, but let's see where the code goes after the additional tests provided by @taueres