如何获得纯PHP和PDO没有N + 1点的低效率相关的对象?

时间:2019-02-02 15:20:35

标签: php oop pdo model

摘要

如何使用纯PHP和PDO获取对象及其相关对象,而不会遇到N + 1地狱?


我想了解实例化对象及其关系的根本途径,而无需使用框架或ORM。通常,当我询问并提供示例时,我会收到诸如“您永远不会那样使用结果”之类的响应,这让我大为震惊,因为这些示例在现实世界中如此普遍,因此我每天都会在过程代码中使用这些类型的查询。

因此,假设您有一个User类和一个PhoneNumber类。一个用户可以有多个电话号码。好吧,这是一个完全正常的现实用例。您想获取所有User和相关的PhoneNumber。你将如何从PDO检索结果,实例化用户和填充每个用户private $phoneNumbers = [];财产?我是新手OOP心中想说的foreach在一个关联的结果集,由USER_ID排序,然后遍历结果集的实例化一个新用户(),并设置其属性和遍历每个用户,实例化一个新的******中国(),并将其推到财产,但最肯定似乎“错误”。

当人们说这些类型的例子不是司空见惯(仍然感到震惊)时,我总是回到亚马逊/电子商务的例子。你有一个订单历史记录页面。那将有一个Order类和一个OrderItem类。您可以在单个页面上查看所有订单上所有OrderOrderItem的购买。

由于我不想因为没有任何代码而受到抨击,所以这绝对是不正确的,但这就是我试图理解实例化对象结果以及相关结果的“方法”。原始对象的

在程序我可以在一查询溶液做到这一点。我期望在OOP解决方案中最多两个查询。一个得到所有订单的用户和一个得到所有的OrderItems。在下面的代码片段。 $ clientOrders包含来自客户端的所有订单的。太好了,我现在所有的order_id是我需要从获得的OrderItems但如何获得的的结果,并填充它们对每个订单?

$orderRepo = new OrderRepository(); // This is a must.
$orderItemsRepo = new OrderItemRepository(); // Unsure if this would be needed/used.

$clientOrders = $orderRepo->byClientId($clientId);

foreach ($clientOrders as $clientOrder) {

$orderItemsRepo->byOrderId($clientOrder->getId()) // definitely wrong

}

所以,我相当有信心,在foreach是不妥当的,我认为它会被用来实例OrderItem S和他们推到每个$clientOrder,无论哪种方式,我肯定知道我不会被查询DB中的迭代。

1 个答案:

答案 0 :(得分:0)

什么你试图避免对每个订单的订单项目的循环查询。

您想有疑问的恒定数量,而不是N+1,其中1是订单列表和N需要查询,以便获取每个订单顺序的原始查询您遍历订单的商品。

我建议不要对每个$clientOrder进行一次查询:

  • 批次的所有$clientOrder id的一起在一个数组,然后结合起来,使逗号分隔的列表
  • 创建一个选择中,可以让你在你的ID
  • 的名单通过
  • 假设项有一个客户订单ID,然后通过的订单项目的结果循环,并将它们添加到适当的顺序$clientOrder手动

无论是订单项或用户的电话号码我假设你有2个表,其中它是一个基于外部对象(用户或订单)的ID的一个一对多的关系。

在子句是不容易的我最后一次检查PDO,但这里有一个链接显示一个例子:https://stackoverflow.com/a/14767651/722263

如果你这样做,你现在去1个查询拉客户订单和1个查询拉所有订单中的商品(或用户的电话号码,如前面在您的文章)。

这里的一些伪PHP:

$orders = [];

for(getAllOrders() as $order) {
   $orders[$order->getId()] = $order;
}

for(getAllOrderItemsByOrderIds(array_keys($orders)) as $orderItem) {
   $order[$orderItem->getOrderId()].addOrderItem($orderItem);   
}

getAllOrders()调用单个 PDO查询以使用获取类获取所有订单的情况下,getAllOrderItemsByOrderIds()创建一个单个 PDO选择以返回全部订单商品也带有访存类。

您甚至可以将两个查询合并为一个查询(如您所述),返回大量结果集,并手动构建每个类,而不使用PDO FETCH_CLASS,但是我发现这种事情是微优化,尤其是当您会遇到越来越多的关系。我尽量保持查询绑定到他们构建,而不是返回巨平的结果集多种不同的实体,除非有重大的性能需要涉及的对象。可以更容易地处理高速缓存以及