2014-07-11 59 views
0

在Symfony2中的表单类型中,我想包含一个实体字段和查询生成器,以便选择要显示的行。 我的查询很复杂,我无法弄清楚使用查询生成器的方式,我想使用DQL。 不幸的是,我不能做$repository->createQuery(...)$repository->getEntityManager()->createQuery()。只有$repository->createQueryBuilder(...)有效。 有没有办法解决这个问题?无法从表单构建器的存储库中获取EntityManager

顺便说一句,如果你碰巧发现执行我的SQL请求,这将是非常有用的一个更聪明的方式(但不是这个问题的话题!)

public function buildForm(FormBuilderInterface $builder, array $options) 
{ 
    $user = $this->user; 
    $builder 
     ->add('friend', 'entity', 
      array(
       'class' => 'MyappUserBundle:User', 
       'label' => 'User', 
       'query_builder' => function(EntityRepository $repository) use ($user) { 
        $query = $repository->getEntityManager()->createQuery(" 
         SELECT u 
          FROM MyappUserBundle:User u 
         WHERE 
          u.id NOT IN (SELECT friend FROM MyappUserBundle:Friendrequest WHERE user = :user) 
         AND 
          u.id NOT IN (SELECT user FROM MyappUserBundle:Friendrequest WHERE friend = :user) 
         AND 
          u.id != :user 
         ") 
         ->setParameter('user', $user) 
         ; 

        return $query; 
       } 
       ) 
      ) 
     ; 
} 

回答

4

如何解决query_builder问题(和问题一般)

我们知道我们有权访问您的实体的EntityRepository类。因此,我们可以轻松找到可公开访问的方法。

因此,我们可以搜索Google“doctrine api entityrepository”,这导致我们到the EntityRepository class API documentation on the doctrine site

在该页面上,我们可以找到哪种方法可公开访问,您可以找到3种关于创建查询的方法,其中之一是createNativeNamedQuery

根据文档,此方法返回一个Doctrine\ORM\NativeQuery的实例,单击该实例将导致使用其他方法创建另一个页面。您可以看到NativeQuery有一个setQuery方法并从Doctrine\ORM\AbstractQuery继承了其他方法的列表。

现在,我们可能拥有了所有我们需要的信息,因此我们可以提出一个可能有效的解决方案(如果不是,那么我们可能真的很接近)。

// The closure expects \Doctrine\ORM\QueryBuilder to be returned 
// so we need to create a named query first then create a list of ids 
// and pass it to a query builder. 
'query_builder' => function(EntityRepository $repository) use ($user) { 
    // Query for user ids with sub queries 
    $results = $repository 
     ->createNativeNamedQuery('u') 
     ->setSQL(" 
      SELECT u.id 
      FROM user AS u 
      WHERE u.id NOT IN (
       SELECT friend FROM friend_request_table WHERE user = :user 
      ) 
      AND u.id NOT IN (
       SELECT user FROM friend_request_table WHERE friend = :user 
      ) 
      AND u.id != :user 
     ") 
     ->setParameter('user', $user->getId()) 
     ->getArrayResult(); 

    // Build an array of IDs 
    $ids = array_map(function ($row) { 
     return $row['id']; 
    }, $results); 

    // Returns a QueryBuilder 
    return $repository->createQueryBuilder('u') 
     ->where('u.id IN (:ids)') 
     ->setParameter('ids', $ids); 
} 

缺点是执行了两个查询。封闭期望返回QueryBuilder,所以我们需要先创建一个原始查询,然后将结果传递给QueryBuilder

最重要的是,原生查询意味着原始SQL查询,因此您不能使用实体注释,并且您的查询也可能不支持跨不同数据库。

如果你想我的意见,我认为你可以重构你的查询并删除子查询。删除子查询并将其结果作为参数传递将允许您使用DQL并因此支持更多类型的数据库。

+0

谢谢你的详细解答。我做了谷歌的EntityRepository类的参考,但它留给我2个问题(我应该在我的问题中提到)。首先,我真的不喜欢使用'NativeNamedQuery'(谁做?),其次,我不明白为什么调用'getEntityManager'会抛出一个错误500'未定义的方法'getEntityManager'“。我现在将使用您的解决方案。就删除子查询而言,我完全支持它,但我不知道在表单构建器中访问不同存储库的方法... – arnaudrg

+0

getEntityManager方法具有受保护的范围,因此您可以在这个上下文中调用它(认为'Doctrine'>'EntityManager'>'Repository',所以设计它们可能不希望Repository类访问另一个Repository并且做一些疯狂的事情)。你也可以访问'createQueryBuilder'和'createNamedQuery'方法,尽管你可能无法做子查询(我从来没有尝试过,因为子查询不是很好)。当你创建表单时,你可以将选项传递给构造函数,使用你想要放入'NOT IN'语句的id。 –

+0

这很有道理。再次感谢! – arnaudrg

相关问题