简体   繁体   中英

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 :)

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.

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();
}

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM