你如何使用纯PHP和PDO对象及其相关对象,而不会在N + 1地狱?
我想了解,以实例化对象和他们的关系不使用框架或ORM的根本途径。通常,当我问及提供的例子,我得到这样的“你永远不会使用结果的方式”,等它吹拂我的心灵考虑的例子是我每天在程序代码中使用这些类型的查询的现实世界中如此普遍的反应.. 。
所以,让我们说你有一个User类和一个PhoneNumber类。用户可以有很多PHONENUMBERS。嗯,这是一个完全正常的现实世界中的用例。你想获得的所有User
s及相关PhoneNumber
s。你将如何从PDO检索结果,实例化用户和填充每个用户的private $phoneNumbers = [];
财产?我是新手OOP心中想说的foreach在一个关联的结果集,由USER_ID排序,然后遍历结果集的实例化一个新用户(),并设置其属性和遍历每个用户,实例化一个新的******中国(),并将其推到财产,但最肯定似乎“错误”。
当人们说,这些类型的实例是不常见的,地方(仍然震惊),我总是回到亚马逊/电子商务的例子。你有一个订单历史记录页面。这将有一个Order类和OrderItem的类。您可以查看所有Order
s和所有订单的OrderItem
s申购的单一页面上。
因为我不想让扑上上没有任何代码做,这肯定是不正确,但是这是我想了解“如何做”的如何实例的原始对象的对象的结果,以及相关的结果。
在程序上,我可以在一个查询解决方案做到这一点。我期望在一个面向对象的解决方案最大的两个查询。一个得到所有订单的用户和一个得到所有的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
,无论哪种方式,我肯定知道我不会被查询数据库中的迭代。
什么你试图避免的,是对每一个订单的订单项循环查询。
你想有疑问的恒定数量,而不是N+1
其中1
是订单列表和N
让每个订单订购的项目,如您遍历订单所需要的子查询的原始查询。
我建议,而不是每一个$clientOrder
查询:
$clientOrder
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的,但我觉得这样的事情微的优化,尤其是当你得到更多的更关系来处理。我尽量保持查询绑定到他们构建,而不是返回巨平的结果集多种不同的实体,除非有重大的性能需要涉及的对象。使得它更容易处理,缓存为好。