I have an entity called foo
which has an OneToMany association with an entity called bar
that is accessible as $foo->getBar()
(an ArrayCollection). Normally calling $foo->getBar()
would trigger a Lazy Loading of associated bar
entities (if they weren't joined originally).
How can I check if bar
has been loaded without triggering a Lazy Load? I don't need the associated entities, if they weren't loaded originally, and I don't want them to load, I just want to know IF they were loaded.
In the fooRepository
I have a method called getFooWithBar()
and that has a join which loads all the bars
as an ArrayCollection and returns foo
with all the associated bar
entities. But if I just call a simpler method like getFooById()
with a simple query, the bar
entities were not loaded with a join, so they are not contained in $foo
.
So in another controller I have $foo, and I want to check if getBar()
has associated entities loaded yet, but I do not want to trigger the Lazy Loading. If it doesn't have associated entities, I don't want them. I just need to know IF they have been loaded.
NOTE: I also do not want to turn off Lazy Loading on the entity association for all instances.
I put this magic getter method in my entity:
public function __get($property) {
return isset($this->$property) ? $this->$property : null;
}
Which theoretically lets me check if the property is set (or if it's still the default private declaration). And this works when my entity is the owning side. But if it's the inverse side, $this->property is never set. Doctrine does some fancy stuff so that when you do getProperty()
it's looking at the data somewhere else. I figured this out because this function works when it's the owning side (it returns a proxy of the associated entity), but it returns null
when the associated entity is owned by the other entity.
After years of testing our code (responding to Doctrine changes) the following is the best solution we could come up with to check if an association has been loaded, WITHOUT trigger LazyLoad. None of this stuff is documented in Doctrine (unfortunately), so you have to look at the source code and/or play with the code.
In the end there are many different types of different associations that could be loaded from *ToMany (PersistentCollection) or *ToOne associations (Proxy or direct entity). This means we need to create a method that checks for all the possibilities (that we are currently aware of in our app). We created a trait that we add to all our entities, so we can call $entity->isLoaded($propertyName)
to check if it's loaded.
public function isLoaded($property)
{
// *ToMany Association are PersistentCollection and will have the isInitialized property as true if it's loaded
if ($this->{$property} instanceof PersistentCollection) {
return $this->{$property}->isInitialized();
}
// *ToOne Associations are (sometimes) Proxy and will be marked as __isInitialized() when they are loaded
if ($this->{$property} instanceof Proxy) {
return $this->{$property}->__isInitialized();
}
// NOTE: Doctrine Associations will not be ArrayCollections. And they don't implement isInitalized so we really
// can tell with certainty whether it's initialized or loaded. But if you join entities manually and want to check
// you will need to set an internal mapper that records when you've loaded them. You could return true if count > 0
if ($this->{$property} instanceof ArrayCollection) {
// NOTE: __isLoaded[$property] is an internal property we record on the Setter of special properties we know are ArrayCollections
return (!empty($this->__isLoaded[$property]) || $this->{$property}->count() > 0);
}
// NOTE: there are never any Collections that aren't ArrayCollection or PersistentCollection (and it does no good to check because they won't have isInitialized() on them anyway
// If it's an object after the checks above, we know it's not NULL and thus it is "probably" loaded because we know it's not a Proxy, PersistentCollection or ArrayCollection
if (is_object($this->{$property})) {
return true;
}
// If it's not null, return true, otherwise false. A null regular property could return false, but it's not an Entity or Collection so indeed it is not loaded.
return !is_null($this->{$property});
}
When you load your foo object, bar will be an instance of Doctrine\ORM\PersistentCollection
. You can call the isInitialized()
method on this collection to find out if has been initialized.
For Associations that are an ArrayCollection
:
$initialized = $foo->getBar()->isInitialized();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With