Kaz
Kaz

Reputation: 748

Merge 2 queries in 1 Doctrine DQL

I'm using Doctrine 2.4 with Symfony 2.8 and I'm trying to build a friendship system.

I have my MateRelationship entity :

class MateRelationship
{
    /**
     * @var integer
     *
     * @ORM\Column(type="integer", name="id")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User")
     * @ORM\JoinColumn(name="sender", referencedColumnName="id", nullable=false)
     *
     */
    private $sender;

    /**
     * @ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User")
     * @ORM\JoinColumn(name="receiver", referencedColumnName="id", nullable=false)
     *
     */
    private $receiver;

    /**
     * @var \DateTime
     *
     * @ORM\Column(type="datetime", nullable=false)
     */
    private $date;

    /**
     * @var boolean
     *
     * @ORM\Column(type="boolean", nullable=false)
     */
    private $invitationAccepted;

My goal is to make a DQL query to get all accepted friends relationship for a given user (no matter if the user is sender or receiver) .

I managed to do it with 2 queries and by merging results, but it's not optimized and it's harder to implement $limit this way.

public function getMates($user, $limit = 0){

    $query1 = $this->_em->createQuery('SELECT m, u.nickname, u.username FROM AcmeUserBundle:MateRelationship m JOIN m.receiver u WHERE m.invitationAccepted = 1 AND m.sender = :user')
            ->setParameter('user', $user);

    try{
        $result1 = $query1->getResult();
    }
    catch(\Doctrine\ORM\NoResultException $e){
        $result1 = [];
    }

    $query2 = $this->_em->createQuery('SELECT m, u.nickname, u.username FROM AcmeUserBundle:MateRelationship m JOIN m.sender u WHERE m.invitationAccepted = 1 AND m.receiver = :user')
            ->setParameter('user', $user);

    try{
        $result2 = $query2->getResult();
    }
    catch(\Doctrine\ORM\NoResultException $e){
        $result2 = [];
    }

    return array_merge($result1, $result2);
}

I'd like to have a single query so I could use ->setMaxResults($limit) but I have no idea how to merge these 2 queries in 1. Thanks for your help :)

Upvotes: 3

Views: 5465

Answers (3)

Alok Patel
Alok Patel

Reputation: 8022

You can simply do it by putting OR when you match :user. That can be either receiver OR sender.

SELECT m, usen.nickname, usen.username, urec.nickname, urec.username
FROM AcmeUserBundle:MateRelationship m 
JOIN m.sender usen 
JOIN m.receiver urec 
WHERE m.invitationAccepted = 1 AND 
(m.receiver = :user OR m.sender= :user)

Upvotes: -2

lolmx
lolmx

Reputation: 521

Here's a proposal using the doctrine query builder

public function getMates($user, $limit = 0)
{
    // Create query builder
    $queryBuilder = $this->_em->getRepository("AcmeUserBundle:MateRelationship")->createQueryBuilder('m');

    // Create 'or' expression
    $or = $queryBuilder->expr()->orX();
    $or
        ->add('m.sender = :user')
        ->add('m.receiver = :user');

    // Create the query
    $queryBuilder
        ->andWhere('m.invitationAccepted = 1')
        ->andWhere($or)
        ->setMaxResults($limit)
        ->setParameter('user', $user);

    // Return the result
    return $queryBuilder->getQuery()->getResult();
}

Upvotes: 4

yceruto
yceruto

Reputation: 9585

You can do this using LEFT JOIN and CASE to obtain the valid user information:

    public function getMates($user, $limit = 0)
    {
        $dql = <<<DQL
            SELECT 
               m, 
               CASE usen.nickname IS NULL WHEN TRUE THEN urec.nickname ELSE usen.nickname END AS nickname, 
               CASE usen.username IS NULL WHEN TRUE THEN urec.username ELSE usen.username END AS username, 
               CASE usen.nickname IS NULL WHEN TRUE THEN 'sender' ELSE 'receiver' END AS relationship
            FROM AcmeUserBundle:MateRelationship m 
            LEFT JOIN m.sender usen 
            LEFT JOIN m.receiver urec 
            WHERE m.invitationAccepted = 1 AND (m.receiver = :user OR m.sender= :user)
DQL;

        return $this->_em->createQuery($dql)
            ->setParameter('user', $user)
            ->setMaxResults($limit)
            ->getResult();
    }

If the $result is empty it's already an empty array [].

Note: The NoResultException exception only is thrown for getOneOrNullResult, getSingleResult or getSingleScalarResult methods they are invoked.

Upvotes: 1

Related Questions