Doctrine:不指定类完全限定名 (FQN) 的 DQL 查询

Posted

技术标签:

【中文标题】Doctrine:不指定类完全限定名 (FQN) 的 DQL 查询【英文标题】:Doctrine: DQL query without specifying class fully qualified name (FQN) 【发布时间】:2020-11-26 12:10:41 【问题描述】:

有没有办法在不指定类的完全限定名的情况下编写 DQL 查询?

有没有办法创建以下查询:

return $this->getEntityManager()->createQuery(
    'SELECT i
     FROM Company\AccountingBundle\Entity\Invoice i
     WHERE i.id = :id'
)->setParameter('id', 1)
    ->getResult();

像这样:

use Company\AccountingBundle\Entity\Invoice;
....

return $this->getEntityManager()->createQuery(
    'SELECT i
     FROM Invoice i
     WHERE i.id = :id'
)->setParameter('id', 1)
    ->getResult();

或类似的东西。我真的会避免在 DQL 中设置完整的 FQN。

但我没有找到任何“setClass”或其他方法来执行此操作。

编辑上下文

实际上,它是一个 Symfony 项目,查询位于 Repository 类中。我有一个实体的Doctrine XML定义,一个实体和实体存储库也在XML中定义,但我总是需要明确定义FQN,有什么想法吗?

【问题讨论】:

可以使用字符串拼接:$work_class="Company\AccountingBundle\Entity\Invoice"; return $this->getEntityManager()->createQuery( 'SELECT i FROM ' . $work_class . ' i WHERE i.id = :id' )->setParameter('id', 1) ->getResult(); 别想了,用heredoc或sprintf构建您的查询字符串,然后将Invoice::class传递给占位符值。 另一种方法是使用存储库,因为存储库始终与实体绑定,因此您无需明确声明它们。但我不知道您的应用程序的状态或要求,所以这需要您进行研究。 @El_Vanja 感谢您的评论,您是对的,我没有解释上下文!我的错。事实上,它是一个 Symfony 项目,查询位于 Repository 类中。我有一个实体的Doctrine XML定义,一个实体和实体存储库也在XML中定义,但我总是需要明确定义FQN,有什么想法吗? 您是否查看过您的 Doctrine 配置中的 alias 参数?官方例子here 【参考方案1】:

Doctrine 提供了一种将实体命名空间别名为更简单、更短的名称的方法,以便在 DQL 查询或存储库访问中使用。

您可以在.yaml的Doctrine配置中设置实体命名空间的别名

doctrine:
  orm:
    auto_generate_proxy_classes: true
    naming_strategy: doctrine.orm.naming_strategy.underscore
    auto_mapping: true
    mappings:
        Company:
            is_bundle: false
            type: annotation
            dir: '%kernel.project_dir%/src/Company/AccountingBundle/Entity'
            prefix: 'App\Company\AccountingBundle\Entity'
            alias: AC

然后在 DQL 中,您可以使用“AC:Invoice”:

return $this->getEntityManager()->createQuery(
  'SELECT i
  FROM AC:Invoice i
  WHERE i.id = :id'
  )->setParameter('id', 1)
  ->getResult();

参考文献

Symfony bundle Mapping Configuration

Symfony Doctrine ORM Configuration

【讨论】:

【参考方案2】:

假设您的存储库类扩展 \Doctrine\ORM\EntityRepository

你试过类似的东西吗?

return $this->getEntityManager()
       ->createQuery('SELECT i FROM '.$this->getEntityName().' i where i.id = :id')
       ->setParameter('id',1)
       ->getResult();

【讨论】:

【参考方案3】:

Doctrine 方法是使用实​​体存储库。如果您的实体配置为绑定到存储库,例如:

/**
 * @ORM\Entity(repositoryClass=InvoiceRepository::class)
 */
class Invoice

然后在该存储库中,如果您使用查询生成器,则该实体是 automatically tied to the query:

$qb = $this->createQueryBuilder('i') // 'i' is just an alias here
    ->where('i.id = :id')
    ->setParameter('id', $id);

要查看它提供的所有选项,您可以在查询生成器here 上阅读更多信息。


快速替代方案(不包括 Doctrine 功能),出于演示目的,我将留在这里:

1.使用sprintf

$q = sprintf('SELECT * FROM %s WHERE id = :id', Invoice::class);

return $this->getEntityManager()
    ->createQuery($q)
    ->setParameter('id', 1)
    ->getResult();

2。使用HEREDOC 语法

$entity = Invoice::class;
$q = <<<SQL
    SELECT *
    FROM $entity
    WHERE id = :id
SQL;

return $this->getEntityManager()
    ->createQuery($q)
    ->setParameter('id', 1)
    ->getResult();

在这两种情况下,查询变量的输出都是SELECT * FROM App\Entity\Invoice WHERE id = :id

请注意,您必须为HEREDOC 初始化一个变量,因为它不能直接评估Invoice::class。该选项绝对适用于较长的查询(特别是因为它突出显示 SQL 关键字),而 sprintf 可用于简短、直接的查询。

【讨论】:

好的,总而言之,使用 DQL 无法做到这一点。遗憾的是,QueryBuilder 允许自动检索实体,但不使用 DQL 语法。目前,唯一的方法是像其他人的建议一样更换课程。我会等几天,看看其他人是否会出现另一个想法。在所有情况下都感谢您。 @sdespont 查询构建器在内部将其转换为 DQL,如果需要,您可以获取使用 $dql = $qb-&gt;getDql(); 构建的内容。如果您特别想编写 DQL,那么是的,我的想法很新鲜。也许会有更多了解教义的人出现。无论如何都欢迎你。【参考方案4】:

如果您明确需要 DQL,我深表歉意,但我没有看到有人提到 DBAL QueryBuilder。

这是您的查询,在 Repository 类中,使用 DBAL QueryBuilder:

return $this->getEntityManager()
->getConnection() //
->createQueryBuilder() // This is different query builder that doesn't automatically assume Entity
->select('i')
->from('invoice', 'i') // Specify the table
->where('i.id = :id') // Rest of the syntax should be similar to DQL but you need to query against table columns, not properties (like in DQL) - for example, in DQL: ... where('firstName = :firstName') ... in DBAL: where('first_name = :firstName')
->setParameter('id', 1)
->execute()->fetchAllAssociative(); // Double check if you can fetch as stdClass

无论如何,DBAL QueryBuilder 与 Doctrine Query Builder 的不同之处在于更接近数据库层。

关于 DBAL QueryBuilder 的更多信息:https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/query-builder.html#sql-query-builder

【讨论】:

谢谢,但我真的很想使用 DQL。原因是因为对于不每天都使用这种语法的人来说,具有连接和嵌套条件 AND / OR(包括 AND)的复合查询很难编写和阅读。

以上是关于Doctrine:不指定类完全限定名 (FQN) 的 DQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

通过 maven 导入本地 jar 给出类的完全限定名

动态打印完全限定的类名和方法名[重复]

如何使用Ant获取完全限定的主机名

JVM--双亲委派机制

如何完全限定包名与本地成员名冲突的类?

第五章 SQL定义表(一)