1

I have an entity Parent with oneToMany relation to Child which is related to User.

Child collection in Parent is indexed with user_id, which means, it utilizes indexBy mapping option.

Entity\Parent:
  type: entity
  oneToMany:
    children:
      targetEntity: Entity\Child
      mappedBy: parent
      indexBy: user_id 
      cascade: [ persist ]

Now, in Parent I'd like a method which would tell me if there is a Child for particular User in its collection. To do that I have following code in Parent:

class Parent {
    public function hasChild(User $user) { 
        return isset($this->children[$user->getId()]);
    }
}

This works as expected.

But there's a performance issue with this approach. When I access Parent::children, Doctrine loads whole collection, which may be a few thousands or Child instances.

It there any way to to check that without loading whole collection, but keeping the current interface? I want it to be done via Parent class, not Child repository etc.

Jakub Matczak
  • 15,341
  • 5
  • 46
  • 64
  • I don't remember Doctrine, but your approach isn't good, think sql, then think orm and optimize, you just have to count child where parent = user – amdev Aug 25 '16 at 12:53
  • @amdev The purpose of ORM is completely opposite by definition. Think objects first. Then anything else. – Jakub Matczak Aug 25 '16 at 12:55
  • I misspoke, orm are powerfull, but you have to understand what sql is generated behind, and if you know what is good you know what to do. Some kind of dao like ChildDao.findChildByParent($parent,$user) will return your child object if exist or null if not. Obiously you never manipulate sql querying here it's a basic operation. But if you want to keep your way of course parent->children(user) foreach all the collection until your child is found and it's a lost of performance. – amdev Aug 25 '16 at 13:16
  • You're wrong again. R. Cruz gave the correct answer. ;-) – Jakub Matczak Aug 25 '16 at 13:18
  • What's wrong ? it's not an answer it's an advice, that why I just comment, every developer should display the sql generated for understand what happend and what not to do. Extra_lazy seems good and countain() or count() are features which just did what I said upper, it's not magic. Something like $em->createQueryBuilder()->select('c')->from('Entity\Child')->where('c.parent = :parent')->and('c.id :id')->setParameter('parent',$this)->setParameter('id':$user->getId()) – amdev Aug 25 '16 at 13:47
  • Though I'm trying to find out the performance of lazy loading with condition join for getting the count...But in your case, why not `return $user->getParent() == $this;` ? – Yarco Aug 07 '17 at 06:16

1 Answers1

3

Have you looked at Extra Lazy Associations?

New in version 2.1.

In many cases associations between entities can get pretty large. Even in a simple scenario like a blog. where posts can be commented, you always have to assume that a post draws hundreds of comments. In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This can lead to pretty serious performance problems, if your associations contain several hundreds or thousands of entities.

With Doctrine 2.1 a feature called Extra Lazy is introduced for associations. Associations are marked as Lazy by default, which means the whole collection object for an association is populated the first time its accessed. If you mark an association as extra lazy the following methods on collections can be called without triggering a full load of the collection:

  • Collection#contains($entity)
  • Collection#containsKey($key) (available with Doctrine 2.5)
  • Collection#count()
  • Collection#get($key) (available with Doctrine 2.4)
  • Collection#slice($offset, $length = null)
Community
  • 1
  • 1
Rolando Cruz
  • 2,834
  • 1
  • 16
  • 24
  • Thanks, you're correct in general. Unfortunately is seems there's a problem with extra lazy fetching when `indexBy` is a foreign key (which requires using column name like `user_id` instead of `user`), but that's out of scope of this question. – Jakub Matczak Aug 25 '16 at 13:24