Search code examples
symfonydoctrine-ormsymfony4

What is the correct way to get an entity's children's children in Symfony?


I'm using Symfony4. I have a database structure with three tables, with two one-to-many relations relating them. For instance:

House -> Person -> Dog

Each house can contain many persons, each person can own many dogs. (My database isn't really about houses and dogs, the actual table names have been changed to protect the innocent.) Using Doctrine entities, I of course have functions like $house->getPersons() and $person->getDogs(). But what is the correct way to implement a function like $house->getDogs()? There are several solutions I can come up with, but none of them seem like a "good" solution to me:

  • I could simply add a one-to-many relation relating House to Dog. However, this goes against one of the fundamental rules of normal database structure: no redundant data. The relation house->person->dog is already expressed in the database, so adding another relation directly from house->dog is not only redundant, but could potentially lead to a situation where a dog's person lives in one house, but the dog lives in another (which is not something I want).

  • I could just do something like

    foreach($house->getPersons() as $person){
        $person->getDogs();
        // Do something with each dog
        ...
    }
    

    This isn't well optimized, as I'd be running a separate query for each person - potentially a lot of queries - when I could easily be running one query with a couple joins.

  • I could use the HouseRepository or a query builder within the House entity to run a custom query. This, as I understand it, is frowned upon in Symfony. We generally don't want any DQL or use of repositories in the entity classes, correct?

  • I could use the HouseRepository from within services/controllers to run a custom query. This would be a simple way, but feels a little inelegant. Nevertheless, I have a feeling this might be the "right" way.

To sum up: I have a feeling I should be able to somehow put this fairly simple double-join in my entity, and not have to go to the repository level to get this data. To be clear, I'm not asking how to write a DQL join, I'm asking where the right place to put it is, or if there's some clever Symfony way to do this using Criteria or something similar.


Solution

  • If you want call $house->getDogs() you could add a method in your House entity.

    public function getDogs()
    {
        /** @var Doctrine\Common\Collections\ArrayCollection */
        $dogs = new ArrayCollection();
    
        if ($persons = $this->getPersons()) {
            foreach($persons as $person) {
                if ($d = $person->getDogs()) {
                    $dogs->add($d);
                }
            }
        }
    
        return $dogs;
    }