[英]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查询中混合所需的大多数或所有实体的解决方案。
如果任何形式的先前查询中的对象已经在内存中,则即使数据库可能包含更多最新数据,也将使用先前对象。 来自数据库的数据将被丢弃。 如果先前的对象仍然是卸载的代理,甚至会发生这种情况。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.