【问题标题】:Doctrine2 - How can I order by a discriminator column?Doctrine2 - 如何按鉴别器列排序?
【发布时间】:2013-10-16 14:17:10
【问题描述】:

我应该如何在学说存储库查询中按鉴别器列进行排序?

我有一个非常简单的设置, 我有不同类型的付款明细, 它可以是信用卡 (CC) 或借记单 (DO)。

所以我实现了一个单表继承映射策略来实现这一点, 但是当我尝试按鉴别器列排序时,问题就出现了,因为基类中不存在鉴别器列。

存储库功能:

public function getPaymentDetails (ClientContactInterface $clientContact)
{
    $dql = 'SELECT pd
            from
            AccountingBundle:PaymentDetail pd
            JOIN ClientProductBundle:ClientProduct cp
            WITH cp.payment_detail_id = pd.id
            WHERE
            cp.payment_detail_id = pd.id
            and cp.client_contact_id = :client_contact_id
            GROUP BY pd.id
            ORDER BY pd.method_type'; // Since pd.method_type is the discriminator column, I cannot order by it. And I need to be able to.

    $em = $this->getEntityManager();
    $query = $em->createQuery($dql)->setParameter('client_contact_id', $clientContact->getId());
    return $query->getResult();
}

Base PaymentDetail 实体:

/**
 * @ORM\Entity(repositoryClass="AccountingBundle\Repository\PaymentDetailRepository")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\Table(name="PaymentDetails")
 * @ORM\DiscriminatorColumn(name="PaymentMethodType", type="string")
 * @ORM\DiscriminatorMap({ "DO" = "DOPaymentDetail", "CC" = "CCPaymentDetail"})
 */

class PaymentDetail implements PaymentDetailInterface
{

    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected  $id;

    /* etc... */
}

Debit Order PaymentDetail 实体:

/**
 * AccountingBundle\Entity\DOPaymentDetail
 *
 * @ORM\Table(name="PaymentDetails")
 * @ORM\Entity
 */
class DOPaymentDetail extends PaymentDetail implements DOPaymentDetailInterface
{

    /**
     * @var string $account_holder
     *
     * @ORM\Column(name="DOAccountHolder", type="string", length=255)
     */
    protected $account_holder;

    /* etc... */
}

Credit Card PaymentDetail 实体:

/**
 * AccountingBundle\Entity\CCPaymentDetail
 *
 * @ORM\Table(name="PaymentDetails")
 * @ORM\Entity
 */
class CCPaymentDetail extends PaymentDetail implements CCPaymentDetailInterface
{

    /**
     *
     * @var string $card_holder
     *
     * @ORM\Column(name="CCCardHolder", type="string", length=255)
     */
    protected $card_holder;

    /* etc... */
}

当我尝试这样做时,我得到了这个错误,

Error: Class AccountingBundle\Entity\PaymentDetail has no field or association named method_type") 

【问题讨论】:

  • 查看您的鉴别器列声明* @DiscriminatorColumn(name="PaymentMethodType" 并检查数据库中生成列的名称...我很确定列/字段名称是PaymentMethodType 和不是method_type。你能确认一下吗?
  • 我可以确认这一点。但是,当我尝试按技术上在 Doctrine 眼中不存在的列进行排序时,问题不断出现,我尝试在 repo 函数中将 method_type 更改为 PaymentMethodType,我得到了...("[Semantical Error] line 0, col 388 near 'PaymentMethodType': Error: 'PaymentMethodType' is not defined.")
  • 您找到解决方案了吗?
  • 我认为它在stackoverflow.com/questions/5988636/…得到了回答

标签: symfony doctrine-orm doctrine dql


【解决方案1】:

尽管 TYPE 尚未实现到核心原则中,但仍然可以将其实现为自定义用户函数。

已经有人做了出色的工作,implemented 为我们完成了这项工作。以防万一以后资源会被删除,这里稍微调整一下php7+的版本:

<?php
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\SqlWalker;

class TypeFunction extends FunctionNode
{
    /**
     * @var string
     */
    public $dqlAlias;

    public function getSql(SqlWalker $sqlWalker): string
    {
        /** @var ClassMetadataInfo $class */
        $class = $sqlWalker->getQueryComponent($this->dqlAlias)['metadata'];
        $tableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $this->dqlAlias);

        if (!isset($class->discriminatorColumn['name'])) {
            $message = 'TYPE() only supports entities with a discriminator column.';
            throw QueryException::semanticalError($message);
        }

        return $tableAlias . '.' . $class->discriminatorColumn['name'];
    }

    public function parse(Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $this->dqlAlias = $parser->IdentificationVariable();
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }
}

现在您可以通过执行以下操作按鉴别器列排序:

SELECT e, TYPE(e) AS HIDDEN my_type FROM Entity e ORDER BY my_type DESC;

【讨论】:

    【解决方案2】:

    似乎最简单的解决方案(到目前为止)是向基类添加另一个字段并复制鉴别器列值。

    上述TYPE(q) 仅在WHERE 子句中有效。

    【讨论】:

      【解决方案3】:

      您可以尝试使用TYPE()INSTANCE OF 吗?

      相关:https://groups.google.com/forum/#!topic/doctrine-user/JtCbwuN-37o

      但是,这个话题并没有说它是否实现。当时有人说 order by 行不通。

      $dql = 'SELECT pd
              from
              AccountingBundle:PaymentDetail pd
              JOIN ClientProductBundle:ClientProduct cp
              WITH cp.payment_detail_id = pd.id
              WHERE
              cp.payment_detail_id = pd.id
              and cp.client_contact_id = :client_contact_id
              GROUP BY pd.id
              ORDER BY TYPE(pd)';
      

      【讨论】:

      • TYPE() 仍未实现 AFAIK
      猜你喜欢
      • 1970-01-01
      • 2011-04-17
      • 2020-09-15
      • 2013-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多