繁体   English   中英

如何在不忽略联接条件的情况下阅读主义联接查询结果?

[英]How to read doctrine join query results without ignoring join conditions?

我有一个带有联接条件的联接查询。 它工作正常,并且getResult()使我可以访问查询结果。 结果也很好(在phpmyadmin上查看)。

$em = $this->getDoctrine()->getManager();
$allRows = $em->getRepository('CampaignBundle:Kpi')->createQueryBuilder('k')
            ->leftJoin('k.countryWeights', 'w', 'WITH', 'w.country = :country')
            ->setParameter('country', $country)
            ->addSelect('w')
            ->getQuery()->getResult();

联接的原因是,我想检查另一个表(或实体)是否具有相关数据,并且如果要存在,我想将其包括在结果中。 这正是左联接所做的。

使用查询结果时出现问题。 例如:

foreach ($allRows as $row) {
    $rowOut = array();
    $rowOut['weight'] = ($row->getCountryWeights()) ? $row->getCountryWeights()[0]->getWeight() : 0;
}

相关实体的属性的getter始终使我可以访问所有相关数据,就像查询中没有联接条件一样。 $row->getCountryWeights()始终为true,因为没有联接条件,联接就不会为空。 如何测试是否加入了任何数据? 如何访问连接的列,例如'weight' 在结果上尝试连接实体的吸气剂似乎不起作用:

Attempted to call an undefined method named "getWeight" 

附加信息:

/**
 * Kpi
 *
 * @ORM\Table(name="kpi")
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class Kpi 
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     *
     * @ORM\OneToMany(targetEntity="CampaignBundle\Entity\KpiWeight", mappedBy="kpi", cascade={"ALL"})
     */
    private $countryWeights;
}

/**
 * KPIWeight
 *
 * @ORM\Table(name="kpi_weight")
 * @ORM\Entity
 */
class KpiWeight
{
    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Kpi", inversedBy="countryWeights")
     * @ORM\JoinColumn(name="kpi", referencedColumnName="id", nullable=false)
     */
    private $kpi;

    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="ZoneBundle\Entity\Country", inversedBy="countryObjectives")
     * @ORM\JoinColumn(name="country", referencedColumnName="id", nullable=false)
     */
    private $country;

    /**
     * @var float
     * @ORM\Column(name="weight", type="float")
     */
    private $weight;
}

这些实体清单不完整,但是所有相关部分都应该在那儿。

由于$row->getCountryWeights()将返回Collection对象,因此您需要测试该集合。

$countryWeights = $row->getCountryWeights(); //Collection object
$rowOut['weight'] = (($firstWeight = $countryWeights->first()) ? $firstWeight->getWeight() : 0);

使用Collection::first()将通过在集合的内部数组上调用reset() Collection::first()来返回false或集合中的第一个条目。

否则,您可以使用$row->getCountryWeights()->isEmpty()来测试Collection是否为空。


范例1:

假设您具有以下数据集;

\\ CampaignBundle \\实体\\ KPI

| id | 
| 1  | 
| 2  | 

\\ CampaignBundle \\ Entity \\ Kpi :: $ countryWeights (OneToMany(targetEntity="CampaignBundle\\Entity\\CountryWeight"))

| kpi_id | country | weight |
| 1      | A       | 1      |
| 1      | B       | 1      |
| 2      | A       | 1      |

默认情况下,该方法将在调用getter时Lazy Load CountryWeight实体关联。

$allRows = $em->getRepository(\CampaignBundle\Entity\Kpi::class)->findAll();
$data = [];
foreach ($allRows as $row) {
    foreach ($row->getCountryWeights() as $weight) {
        $data[$row->getId()][$weight->getCountry()] = $weight->getWeight();
    }
}
dump($data);

将导致CountryWeight关联的延迟加载:

array:2 [▼
  1 => array:2 [▼
    "A" => 1
    "B" => 1
  ]
  2 => array:1 [▼
    "A" => 1
  ]
]

范例2:

但是,当您使用QueryBuilder ,将基于指定的Join:WITH条件限制集合。

$allRows = $em->getRepository(\CampaignBundle\Entity\Kpi::class)->createQueryBuilder('k')
    ->leftJoin('k.countryWeights', 'w', Join::WITH, $expr->eq('w.country', ':country'))
    ->setParameter('country', 'B')//<--- NOTE B is filtered
    ->addSelect('w')
    ->getQuery()
    ->getResult();
$data = [];
foreach ($allRows as $row) {
    $firstWeight = $row->getCountryWeights()->first();
    $data[$row->getId()][$firstWeight ? $firstWeight->getCountry() : null] = $firstWeight ? $firstWeight->getWeight() : 0;
}
dump($data);

将导致:

array:2 [▼
  1 => array:1 [▼
    "B" => 1
  ]
  2 => array:1 [▼
    "" => 0
  ]
]

如果该学说已经检索到CountryWeight关联,例如运行示例1,然后运行示例2,则需要告诉该学说以强制清除它的UnitOfWork [sic]来从数据库中检索过滤的记录,否则它将使用已经加载了用于检索结果的集合。

实现此目的的一种方法是发出

$em->clear(\CampaignBundle\Entity\Kpi::class);

在执行查询之前。 这将清除该对象的所有学说工作单元。 使用Query::HINT_REFRESH ,不会使用国家A完全刷新Kpi :: $ id => 2中的排除值。

已检索到的关联的示例输出。

教义转储


参考文献:

通过Doctrine,您可以遍历域模型中所有对象之间的所有关联。 尚未从数据库加载的对象将替换为延迟加载代理实例。 未加载的Collections也被延迟加载实例代替,该实例在首次访问时会获取所有包含的对象。 但是,依靠延迟加载机制会导致针对数据库执行许多小的查询,这可能会严重影响应用程序的性能。 提取联接是在单个SELECT查询中混合所需的大多数或所有实体的解决方案。

https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/dql-doctrine-query-language.html#joins

如果任何形式的先前查询中的对象已经在内存中,则即使数据库可能包含更多最新数据,也将使用先前对象。 来自数据库的数据将被丢弃。 如果先前的对象仍然是卸载的代理,甚至会发生这种情况。

https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/dql-doctrine-query-language.html#object-hydration

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM