在Doctrine 2中使用类表继承时:如何编写将从子类返回结果的本机SQL查询?

jcr*_*oll 4 php mysql solr symfony doctrine-orm

在文档的示例中提到了对类表继承层次结构执行本机SQL查询,但我将在此处生成相关示例:

<?php

use Doctrine\ORM\Query\ResultSetMapping;

// Equivalent DQL query: "select u from User u where u.name=?1"
// User is a mapped base class for other classes. User owns no associations.
$rsm = new ResultSetMapping;
$rsm->addEntityResult('User', 'u');
$rsm->addFieldResult('u', 'id', 'id');
$rsm->addFieldResult('u', 'name', 'name');
$rsm->addMetaResult('u', 'discr', 'discr'); // discriminator column
$rsm->setDiscriminatorColumn('u', 'discr');

$query = $this->_em->createNativeQuery('SELECT id, name, discr FROM users WHERE name = ?', $rsm);
$query->setParameter(1, 'romanb');

$users = $query->getResult();
Run Code Online (Sandbox Code Playgroud)

从示例:

请注意,在类表继承的情况下,如果结果中的任何对象实际上是User的子类型,则上述示例将导致部分对象.使用DQL时,Doctrine会自动为此映射策略包含必要的连接,但使用本机SQL则由您负责.

上述查询的问题在于,User如果您希望从子类返回上述示例中不返回它们的字段,它将从父类返回字段.我们如何做到这一点?

在我继续前进之前,我花时间创建了一些示例类(一个是父项,另一个是子项),这应该适用于上面的示例查询.让我为你提供:

/**
 * @ORM\Table(name="users",
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({
 *  "regular_user" = "RegularUser",
 *  "administrator" = "Administrator",
 * })
 */
class User
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(name="name", type="string")
     */
    protected $name;
}
Run Code Online (Sandbox Code Playgroud)

和:

/**
 * @ORM\Table(name="regular_users",
 * @ORM\Entity
 */
class RegularUser extends User
{
    /**
     * @ORM\Column(name="phone_number", type="string")
     */
    protected $phoneNumber;
}
Run Code Online (Sandbox Code Playgroud)

因此,我们有一个父类User有两个子类RegularUserAdministrator(注:我只提供了一个子类RegularUser,但我没有为子类的鉴别映射Administrator).

考虑一下:我们不想使用名为'romanb'的用户查询数据库(如上图所示),而是使用像Solr这样的全文搜索引擎来索引数据库.我们查询Solr for romanb,它返回一个用户id的数组,其名称romanb相关性排序.使用这些有序的id,我们想查询数据库并返回常规用户列表,完全水合(即包括他们的电话号码),并按照与Solr返回的id相同的顺序排序.我们应该怎么做?

Doctrine不支持自定义排序,因为它不受所有SQL引擎的支持.它由MySQL这恰好是我们所使用的数据库支持.

请考虑以下Native SQL Query:

$rsm = new ResultSetMapping;
$rsm->addEntityResult('User', 'u');
$rsm->addFieldResult('u', 'id', 'id');
$rsm->addFieldResult('u', 'name', 'name');
$rsm->addMetaResult('u', 'discr', 'discr');
$rsm->setDiscriminatorColumn('u', 'discr');
$rsm->addFieldResult('u', 'phone_number', 'phoneNumber'); // How do we map this result?

// Returned by Solr
$userIds = array(22, 3, 88, 109, 12);

$sql = <<<SQL
  SELECT u.id, u.name, u.discr, r.phone_number
  FROM users u
  INNER JOIN regular_users r ON users.id = regular_users.id
  WHERE u.id IN (?)
  ORDER BY FIELD(u.id, ?); # Custom SQL logic that will return an ordered set
SQL;

$query = $this->_em->createNativeQuery($sql, $rsm);
$query->setParameter(1, $userIds);
$query->setParameter(2, $userIds);

$users = $query->getResult();
Run Code Online (Sandbox Code Playgroud)

虽然SQL很好,但上面的查询将失败并出现错误.如果我们注释掉$rsm->addFieldResult('u', 'phone_number', 'phoneNumber')它会起作用并且RegularUser在结果中返回一个对象(因为所有id都属于常规用户,我们在结果集映射中定义了discriminator列),但这些RegularUser对象都不包含电话号码.

反正有没有做一个会返回完全水合物的查询RegularUser

谢谢阅读.

jcr*_*oll 7

有时候在stackoverflow上写出问题的好处是它可以帮助你更好地思考这个问题,这可以帮助你找到解决方案.

这是上面的一个有效的Native SQL查询:

<?php

$rsm = new ResultSetMapping;
$rsm->addEntityResult('RegularUser', 'r');
$rsm->addFieldResult('r', 'id', 'id');
$rsm->addFieldResult('r', 'name', 'name');
$rsm->addFieldResult('r', 'phone_number', 'phoneNumber');

$userIds = array(22, 3, 88, 109, 12);

$sql = <<<SQL
  SELECT r.id, u.name, r.phone_number
  FROM regular_users r
  INNER JOIN users u ON r.id = u.id
  WHERE u.id IN (?)
  ORDER BY FIELD(u.id, ?); # Custom SQL logic that will return an ordered set
SQL;

$query = $this->_em->createNativeQuery($sql, $rsm);
$query = $this->_em->createNativeQuery($sql, $rsm);
$query->setParameter(1, $userIds);
$query->setParameter(2, $userIds);

$users = $query->getResult();
Run Code Online (Sandbox Code Playgroud)

基本上没有必要涉及鉴别器映射,假设您知道您要查询的ID RegularUser.

我希望这有助于其他人(我知道边缘情况).