我正在编写一个私人消息系统,我遇到了一个问题,我无法正确获取用户的线程列表。
我有3个实体:
线程实体有很多关系:
/**
* @ORM\ManyToMany(targetEntity="Acme\UserBundle\Entity\User", cascade={"persist"})
*/
private $users;
我有一个查询正在努力实现接近我想要的东西:
public function getThreads($user) {
$q = $this->createQueryBuilder('t')
->leftJoin('t.users', 'u')
->addSelect('u')
->where('u.id = :user')
->setParameter('user', $user)
->orderBy('t.date', 'DESC')
->setMaxResults(20);
return $q->getQuery()->getResult();
}
但我在主题列表中得到我的名称:
From | Text | date
----------|----------------|--------
Hotgeart | Hello :) | 31 jan
Hotgeart | Wanna go out? | 20 jan
Hotgeart | I love book | 10 jan
显然我想得到另一个用户的名字而不是我的名字:
From | Text | date
----------|----------------|--------
Homer | Hello :) | 31 jan
Bart | Wanna go out? | 20 jan
Lisa | I love book | 10 jan
我需要在查询中更改哪些内容才能获得正确的结果?
P.S:它可以是DQL查询。
修改完整实体
编辑3 feb:
我有点接近我想要的东西:
public function getThreads($user) {
$q = $this->createQueryBuilder('t');
$q->leftJoin('t.users', 'u', \Doctrine\ORM\Query\Expr\Join::WITH, $q->expr()->eq('u.id', ':user'))
->leftJoin('t.users', 'u2', \Doctrine\ORM\Query\Expr\Join::WITH, $q->expr()->neq('u2.id', ':user'))
->addSelect('partial u2.{xxx, xxx, ...}')
->where('u.id = :user')
->setParameter('user', $user)
->orderBy('t.date', 'DESC')
->setMaxResults(20);
return $q->getQuery()->getResult();
}
它给了我这张桌子:
From | Text | date
----------|----------------|--------
NULL | Hello :) | 31 jan
Homer | Hello :) | 31 jan
NULL | Wanna go out? | 20 jan
Bart | Wanna go out? | 20 jan
NULL | I love book | 10 jan
Lisa | I love book | 10 jan
如果我对第一个 JOIN 有一个->addSelect('partial u2.{xxx, xxx, ...}')
我有相同的表,但是 NULL 被hotgeart(我)替换。
答案 0 :(得分:0)
public function getThreads($user) {
$q = $this->createQueryBuilder('t')
->leftJoin('t.users', 'u')
->addSelect('u')
->where('u.id = :user')
->setParameter('user', $user)//limits result to user $user
->orderBy('t.date', 'DESC')
->setMaxResults(20);
return $q->getQuery()->getResult();
}
您必须将其替换为IN / EXISTS。
答案 1 :(得分:0)
您应该将您的Post
映射更正为User for ManyToOne:
/**
* @ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", cascade={"persist"})
*/
private $user;
您的查询将如下所示:
public function getThreads($user) {
$q = $this->createQueryBuilder('t')
->leftJoin('t.posts', 'p')
->leftJoin('p.user', 'u')
->addSelect('t')
->where('u.id = :user')
->setParameter('user', $user)
->orderBy('t.date', 'DESC')
->setMaxResults(20);
return $q->getQuery()->getResult();
}
通过这种方式,您将只拥有线程实体。您不会直接从Thread实体BTW获得用户,因为您不会事先知道该线程的哪些帖子是由他创作的。
答案 2 :(得分:0)
您可以通过下一个方式获取主题:
public function getThreads($user) {
$em = $this->getEntityManager();
$query = $em->createQuery(
'SELECT t
FROM AcmeMessageBundle:Thread t
WHERE :user MEMBER OF t.users
ORDER BY t.date DESC'
)
->setParameter('user', $user)
->setMaxResults( 20 );
$threads = $query->getResult();
return $threads;
}
你将收到一个Doctrine ArrayCollection of Threads(你需要删除AcmeMessageBundle并输入正确的Bundle名称)。
修改强> 再次阅读你的问题,看到你的例子,我认为你想要的不是线程,是与用户相关的线程的帖子。您可以这样做,在Post和Thread实体之间添加双向关系,如下所示:
在Thread实体中,添加:
/**
* @ORM\OneToMany(targetEntity="\Acme\MessageBundle\Entity\Post", mappedBy="thread")
*/
private $posts;
在构造函数中:
$this->posts = new \Doctrine\Common\Collections\ArrayCollection();
在Post实体中,修改您的行:
/**
* @ORM\ManyToOne(targetEntity="Acme\MessageBundle\Entity\Thread", inversedby="posts")
* @ORM\JoinColumn(nullable=false)
*/
private $thread;
并为Thread实体中的帖子生成getter和setter,这样,您可以在获得上述线程后获取帖子。
答案 3 :(得分:0)
我认为你在这里有一些设计问题。
根据我的理解,线程是由用户启动的讨论。帖子是帖子中的个人信息。
如果您可以解释如何计划工作,那么说出您的实体应如何关联会更容易。据我了解你的问题,
如果消息发生在两个人之间,则不需要多对多关系。您只需要与User
实体中的Thread
具有ManyToOne关联的发件人和收件人属性。您的Post
实体看起来不错。之后回到你的peoblem,
获取用户的线程列表:
$q = $this->createQueryBuilder('t')
->select('s.username', 't.message', 't.date') // assuming the fields
->join('t.sender', 's')
->join('t.receiver', 'u')
->where('u.id = :user')
->setParameter('user', $user)
->orderBy('t.date', 'DESC')
->setMaxResults(20);
return $q->getQuery()->getResult();
答案 4 :(得分:0)
为了让参与thread
的人参与进来,您需要优化一下您的查询。目前,您正在按用户id
过滤以获取用户所在的那些线程。但是,这不会使您在该线程中的其他用户(预期行为)因此出现问题。
你要做的是:
这可以在一个查询中混合使用:
$em = $this
->getDoctrine()
->getManager();// Entity manager, I'm using SF and querying in a controller for a simplicity. You can adapt it to a Repository if you like.
$dql = $em
->getRepository("AcmeDemoBundle:Thread")// Change this to suit you
->createQueryBuilder('t2')
->select('t2.id')
->leftJoin('t2.users', 'u2')
->where('u2.id = :user')
->getDQL();// Subquery that will retrieve those threads in which user with id = $user is involved.
$qb = $em
->getRepository("AcmeDemoBundle:Thread")// Change this to suit you
->createQueryBuilder('t');// Querybuilder object: useful for the 'in' part of the query.
$q2 = $qb
->addSelect('u')
->leftJoin('t.users', 'u')
->andWhere($qb->expr()->in('t.id', $dql))
->setParameter('user', 1)// I force 1 for simplicity, you can use a variable.
->orderBy('t.date', 'DESC')
->setMaxResults(20)
->getQuery()
->getResult();// Retrieve threads in which user is in. Each thread will have in users those users involved in the thread.
此查询将包含user
所在的线程数组。每个线程将在users
变量中包含参与该线程的用户列表。 user
将出现在users
个变量中。如果您想添加->where('u.id <> :user')
,可以在查询中对其进行过滤。
希望它有所帮助。