查询生成器或Closure或DQL,是使用ManyToMany关系来获取这个复杂SQL查询的正确方法吗?

时间:2014-11-19 00:46:43

标签: symfony doctrine-orm doctrine dql

所以我是这两个实体:

class TipoRegistro
{
    /**
     * @ORM\ManyToMany(targetEntity="AppBundle\Entity\TipoTramite", inversedBy="tipoRegistros", cascade={"persist"})
     * @ORM\JoinTable(name="nomencladores.tipo_registro_tipo_tramite", schema="nomencladores",
     *      joinColumns={@ORM\JoinColumn(name="tipo_registro_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="tipo_tramite_id", referencedColumnName="id")}
     * )
     */
    protected $tipoTramites;
}

class TipoTramite
{
    /**
     * @ORM\ManyToMany(targetEntity="AppBundle\Entity\TipoRegistro", mappedBy="tipoTramites", cascade={"persist"})
     */
    protected $tipoRegistros;
}

我需要将所有与TipoRegistro关联的TipoTramite与之前显示的ManyToMany关系相关联,如下面的SQL所示:

SELECT tr.* 
FROM
    nomencladores.tipo_registro tr
LEFT JOIN
    nomencladores.tipo_registro_tipo_tramite AS trtt
ON (trtt.tipo_registro_id = tr."id")
WHERE
    tr.activo = true
AND
    trtt.tipo_tramite_id = 1;

我不知道我是否可以在此处使用自定义存储库,因此我唯一的解决方案是在表单级别使用query_builder参数,这就是我正在做的事情:

'query_builder' => function (EntityRepository $er) {
    return $er->createQueryBuilder('tr')
        ->where('tr.activo = :activo')
        ->leftJoin('tr.tipoTramites', 'ttr')
        ->andWhere("ttr.tipo_tramite_id  = :tramite")
        ->setParameter("tramite", 1)
        ->setParameter('activo', true);
}

但该解决方案会导致此错误:

  

[语义错误]第0行,第121行附近' tipo_tramite_id':错误:   类AppBundle \ Entity \ TipoTramite没有字段或   名为tipo_tramite_id的协会

我也尝试了这个(按照每位用户的建议):

->andWhere("ttr.tipoTramites  = :tramite")

但错误变成了这个:

  

[语义错误]第0行,第120行附近' tipoTramites':错误:无效   PathExpression。 StateFieldPathExpression或   SingleValuedAssociationField预期

此外,我还尝试了其他解决方案:

->andWhere("tr.id = :tramite")

但是这个没有返回正确的值。我在实体字段类型上阅读了关于Closures的内容,但不知道如何在这种情况下实现。我想到的另一个想法是使用DQL和准备语句,但我认为这不能在表单级别使用,所以我没有想法,那么我如何获得这些值?我应该如何建立这个ManyToMany实体之间的关系?

1 个答案:

答案 0 :(得分:2)

当您在实体中创建ManyToMany关系时,Doctrine将生成第三个表作为这两个实体(在@ORM\JoinTable中命名)之间的中介,其中包含两个字段(在joinColumns中定义)。您无法直接访问第三个表中的字段或列,但它们都将由DQL中的Doctrine处理。
这意味着在您的DQL中,您只需要在where子句中设置联合表id而不是真实的字段名称,Doctrine将生成正确的SQL:

'query_builder' => function (EntityRepository $er) {
   return $er->createQueryBuilder('tr')
     ->where('tr.activo = :activo')
     ->leftJoin('tr.tipoTramites', 'ttr')
     ->andWhere("ttr.id  = :tramite")
     ->setParameter("tramite", 1)
     ->setParameter('activo', true);
}

这将生成正确的SQL 唯一的提示是您无权访问DQL中第三个表的生成字段,但它将由Doctrine处理,您可以使用连接表ID