映射和DQL

时间:2013-05-03 22:51:14

标签: symfony orm doctrine-orm dql

我有3个表 - 用户,区域和联系人。联系人可以属于用户或区域。用户可以属于许多区域。

我想提取属于用户的所有联系人(如数据库中明确定义的),以及与用户属于同一区域的所有联系人。

我可以在数据库映射上获得一些新的视角,以及我需要在DQL中编写的查询以获得我想要的内容。我在数据库映射中做错了吗?

我绝对是一个SQL用户,能够轻松地在纯SQL中获取我想要的东西。在简单的SQL中,这是我想要做的:

 select c.* from contact c LEFT JOIN user_area ua ON c.area_id=ua.area_id where (ua.user_id=XXX OR c.user_id=XXX);

用户

/**
 * @ORM\ManyToMany(targetEntity="area", inversedBy="areas")
 * @ORM\JoinTable(name="user_area",
 *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="area_id", referencedColumnName="id")}
 *      )
 */
private $areas;

/**
 * @ORM\OneToMany(targetEntity="Contact", mappedBy="user")
 */
private $contacts;

/**
 * @ORM\ManyToOne(targetEntity="Area")
 * @ORM\JoinColumn(name="area_id", referencedColumnName="id")
 */
private $area;

/**
 * @ORM\ManyToOne(targetEntity="User", inversedBy="Contacts")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
 */
private $user;

AREA

/**
 * @ORM\ManyToMany(targetEntity="User", mappedBy="users")
 */
private $users;

/**
 * @ORM\OneToMany(targetEntity="Contact", mappedBy="area")
 */
private $contacts;

我遇到的主要问题是DQL真的希望你查询一个对象,在SQL中查询用户/区域关系表来获得我想要的东西就更容易了。我尝试编写一个查询,从联系人中提取区域,然后从联系人中提取用户,然后从区域中提取用户,但我收到一条错误消息,“用户”不是我的区域对象中的已定义索引。再一次,我是一个学说新手,所以我可能做错了。

这是我在Symfony中的User对象中的查询尝试:

    $qb = $em->createQueryBuilder()
        ->addSelect('c')
        ->from('MyBundle:Contact', 'c')
        ->leftJoin('c.area', 'ca')
        ->leftJoin('c.user', 'cu')
        ->leftJoin('ca.users', 'cau')
        ->add('where', 'c.user = ?1 OR cau.id = ?1')
        ->add('orderBy', 'c.name')
        ->setParameter(1, $this->getId());

2 个答案:

答案 0 :(得分:1)

有人应该打电话给我提供之前的答案。虽然它完成了工作,但我完全正确,它没有得到优化。使用该方法的查询需要3秒才能来回数据库(3秒!)。显然,在我的世界中还有很多其他事情正在将性能作为完成这项工作的要求,但事情已经发生了变化。我设法将这个查询分解为两个较小的(Doctrine生成的),每个可能需要0.2或0.3秒。

    $areas = $user->getAreas();

    $qb = $this->getEntityManager()->createQueryBuilder();
    $qb->select('c')
        ->from('MyBundle:Contact', 'c')
        ->where($qb->expr()->in('c.area', '?1'))
        ->orWhere('c.user = ?2')
        ->setParameter(1, $areas->toArray())
        ->setParameter(2, $user);
    $query = $qb->getQuery();
    $result = $query->getResult();
    return $result;

我必须调用$user->getAreas()这一事实添加了一个数据库查询(如果Doctrine还没有这些信息),但是使用Query Builder表达式的代码效果要好得多(0.3s vs. 3s是原始查询时间的10%!)。

我认为我当时缺少的主要概念是查询生成器想要处理您的对象(实体)以及您在实体中定义的属性。来自强大的SQL背景,并且知道我希望Doctrine生成的特定SQL查询,我没有正确地解决问题。

希望这个为期8个月的问题的更新有助于某人!

答案 1 :(得分:0)

事实证明你无法在DQL中获取对象的对象。我需要获取所有“区域”(“联系人”的对象),然后获取该区域的所有“用户”。

在DQL中,您可以指定多个“from()”辅助方法,这就是我完成工作所需要的。

$qb = $em->createQueryBuilder()
    ->addSelect('c')
    ->from('MyBundle:Contact', 'c')
    ->from('MyBundle:User', 'u')
    ->leftJoin('c.area', 'ca')
    ->leftJoin('c.user', 'cu')
    ->leftJoin('u.areas', 'ua')
    ->add('where', 'c.user = ?1 OR (ua.id=ca.id AND u.id = ?1')
    ->add('orderBy', 'c.name')
    ->setParameter(1, $this->getId());

从Doctrine生成的结果SQL似乎没有特别优化,但它完成了工作。如果有人对让Doctrine更好地优化以下查询有任何想法,我很乐意听取意见。

  

SELECT m0_.id AS id0,m0_.name AS name1,m0_.email AS email2,m0_.media_area_id AS media_area_id3,m0_.user_id AS user_id4 FROM contact m0_ LEFT JOIN user u1_ ON m0_.user_id = u1_.id LEFT JOIN区域m2_ ON m0_.area_id = m2_.id,用户u3_ LEFT JOIN user_area u5_ ON u3_.id = u5_.user_id LEFT JOIN区域m4_ ON m4_.id = u5_.area_id WHERE u1_.id =?或(m4_.id = m2_.id和u3_.id =?);