多对多 - 获取用户的线程

时间:2015-01-31 11:14:29

标签: symfony doctrine-orm

我正在编写一个私人消息系统,我遇到了一个问题,我无法正确获取用户的线程列表。

我有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(我)替换。

5 个答案:

答案 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过滤以获取用户所在的那些线程。但是,这不会使您在该线程中的其他用户(预期行为)因此出现问题。

你要做的是:

  1. 首先,找出用户所在的主题。
  2. 二,检索涉及所有用户的线程。
  3. 这可以在一个查询中混合使用:

    $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'),可以在查询中对其进行过滤。

    希望它有所帮助。