如何在不忽略连接条件的情况下读取连接查询结果?

dl5txlt9  于 2021-06-25  发布在  Mysql
关注(0)|答案(1)|浏览(348)

我有一个带有连接条件的连接查询。它运行良好 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' ? 在结果上尝试连接实体的getter似乎不起作用:

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;
}

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

wecizke3

wecizke31#

$row->getCountryWeights() 将返回一个 Collection 对象,则需要测试集合。

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

使用 Collection::first() 会回来的 false 或者调用 reset() 在集合的内部数组上。
否则,可以使用 $row->getCountryWeights()->isEmpty() .
解释
例1:
假设您拥有以下数据集;
\活动包\实体\ 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 |

默认的原则是 `Lazy Load` 调用getter时的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` ,将不会完全刷新kpi中排除的值::$id=>2且国家/地区为a。
已检索关联的示例输出。
![](https://i.stack.imgur.com/UVqcO.png)
参考文献:
条令允许您遍历域模型中所有对象之间的所有关联。尚未从数据库加载的对象将替换为延迟加载代理示例。未加载的集合也被延迟加载示例替换,后者在第一次访问时获取所有包含的对象。但是,依赖延迟加载机制会导致对数据库执行许多小查询,这会显著影响应用程序的性能。fetch join是一种解决方案,可以在单个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-水合作用

相关问题