Reputation: 748
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
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
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
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