Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony/Doctrine relation COUNT in entity

As the title states,

I would like to run 1 query to get results from a table with the count of their respective relationships.

Lets say I have a Person entity with a OneToMany relationship with a Friend entity

The Person entity might look something like the following:

class Person
{
    /**
     * @ORM\OneToMany(...)
     */
    protected $friends;

    public function __construct()
    {
        $this->friends = new ArrayCollection();
    }

    ...
}

A classic SQL solution to what I want to achieve might look like the following:

SELECT p.*, COUNT(f.id) as friendsCount
FROM Persons p
LEFT JOIN Friends f
ON f.person_id = p.id
GROUP BY p.id

Now I am wondering if this could be done in DQL as well and storing the count value into the Person Entity

Lets say I expand the Person entity like: (Keep in mind this is just an idea)

class Person
{
    /**
     * @ORM\OneToMany(...)
     */
    protected $friends;

    protected $friendsCount;

    public method __construct()
    {
        $this->friends = new ArrayCollection();
    }

    ...

    public function getFriendsCount()
    {
        return $this->friendsCount;
    }
}

Now I am having trouble finding how I could populate the count value in the entity from DQL:

SELECT p, /* What comes here ? */
FROM AppBundle\Entity\Person p
LEFT JOIN p.friends f
GROUP BY p.id

PS: I do know I could just do:

$person->getFriends()->count();

And even mark it as extra lazy to get the count result.

I just though this count relationships example demonstrated well what I am trying to do.

(Which is populating the entity's non @ORM\Column properties from dql)

Is this possible with Doctrine ?

Is this breaking some solid principles ? (SRP ?)

Cookie for your thoughs ;)

like image 206
RVandersteen Avatar asked Feb 02 '16 18:02

RVandersteen


1 Answers

You probably just want to select the count as you need it, as described above with $person->getFriends()->count();. However, you can select both an object and a count at the same time - see these Doctrine DQL examples, there is one very similar to what you are doing:

SELECT p, COUNT(p.friends)
FROM AppBundle\Entity\Person p
LEFT JOIN p.friends f
GROUP BY p.id

What should be returned back is an array of arrays with the object and count, like so:

[
    [Person, int],
    [Person, int],
    ...,
]

Your best bet would be to make that a Repository call on your PersonRepository, something like findPersonsWithFriendCount(), which would look like:

public function findPersonsWithFriendCount()
{
    $persons = array();

    $query = $this->_em->createQuery('
        SELECT p, COUNT(p.friends)
        FROM AppBundle\Entity\Person p
        LEFT JOIN p.friends f
        GROUP BY p.id
    ');

    $results = $query->getResult();

    foreach ($results as $result) {
        $person = $result[0];
        $person->setFriendsCount($result[1]);

        $persons[] = $person;
    }

    return $persons;
}

Keep in mind you'd need to add a setFriendsCount() function on your Person object. You could also write a native SQL query and use result set mapping to automatically map your raw result's columns to your entity fields.

like image 68
Jason Roman Avatar answered Jan 01 '23 19:01

Jason Roman