-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Description
Bug Report
| Q | A |
|---|---|
| BC Break | no |
| Version | 2.6.4 |
Summary
I have the following entities:
IntervalGroupw/ oneToMany relation toIntervalType(fetch:EXTRA_LAZY)IntervalTypew/ oneToMany relation toInterval(fetch:EXTRA_LAZY)Intervalw/ oneToMany relation toTask(fetch:EAGER)
On the IntervalGroup repository I'm doing a DQL Query Builder query to load all IntervalGroups, and left join IntervalGroup.intervalTypes and intervalTypes.interval.
By default, the Task relation on Interval will be eager-loaded during hydration, however I don't want Tasks in this case. To counter that I'm using setFetchMode() on the Query object like so:
$query->setFetchMode(Interval::class, 'tasks', ClassMetadataInfo::FETCH_LAZY)Which, as I understand, should prevent the ORM from fetching and hydrating those relations in that query.
Current behavior
Currently, the query hint for the fetch mode is not overriding the fetch mode on the annotation, so Doctrine loads the Interval.tasks relation.
createEntity in the UnitOfWork class is using the fetch value from the annotation, instead of the fetchMode query hint:
// Lines 2859 - 2862 of UnitOfWork.php
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
$this->loadCollection($pColl);
$pColl->takeSnapshot();
}(as seen here)
When I dump($assoc) I can see for the targetEntity = "Task" that $assoc['fetch'] equals 3 (ClassMetadata::FETCH_EAGER) when I specified in setFetchMode() that it should be 2 (ClassMetadata::FETCH_LAZY).
How to reproduce
class IntervalGroup
{
/**
* @ORM\OneToMany(targetEntity="IntervalType", mappedBy="intervalGroup", fetch="EXTRA_LAZY")
*/
private $intervalTypes;
public function __construct()
{
$this->intervalTypes = new ArrayCollection();
}
}class IntervalType
{
/**
* @ORM\ManyToOne(targetEntity="IntervalGroup", inversedBy="intervalTypes", fetch="EAGER")
* @ORM\JoinColumn(name="interval_group_id", referencedColumnName="id", nullable=true)
*/
private $intervalGroup;
/**
* @ORM\OneToMany(targetEntity="Interval", mappedBy="intervalType", fetch="EXTRA_LAZY")
*/
private $intervals;
public function __construct()
{
$this->intervals = new ArrayCollection();
}
}class Interval
{
/**
* @ORM\ManyToOne(targetEntity="IntervalType", inversedBy="intervals")
* @ORM\JoinColumn(name="interval_type_id", referencedColumnName="id", nullable=false)
*/
private $intervalType;
/**
* @ORM\ManyToMany(targetEntity="Task", fetch="EAGER")
* @ORM\JoinTable(name="interval_mapping",
* joinColumns={@JoinColumn(name="interval_id", referencedColumnName="id")},
* inverseJoinColumns={@JoinColumn(name="task_id", referencedColumnName="task_id", unique=true)}
* )
*/
private $tasks;
public function __construct()
{
$this->tasks = new ArrayCollection();
}
}class IntervalGroupRepository extends EntityRepository
{
public function findAllAscendingByName()
{
$qb = $this->createQueryBuilder('ig');
$qb
->select(['ig', 'it', 'i'])
->leftJoin('ig.intervalTypes', 'it')
->leftJoin('it.intervals', 'i');
$query = $qb->getQuery();
$query
->setFetchMode(Interval::class, 'tasks', ClassMetadataInfo::FETCH_LAZY);
return $query->getResult();
}
}Expected behavior
The fetch mode override (as set in setFetchMode()) should be respected during hydration. UnitOfWork::createEntity's $assoc['fetch'] should check the $hints array to see if it is overridden, or use the default annotation value if not.
I believe overriding a fetch annotation should be possible to do, though perhaps I'm doing it the wrong way (in which case I'm happy to be pointed in the right direction!).