如何使用纯PHP和PDO获取对象及其相关对象,而不会遇到N + 1地狱?
我想了解实例化对象及其关系的根本途径,而无需使用框架或ORM。通常,当我询问并提供示例时,我会收到诸如“您永远不会那样使用结果”之类的响应,这让我大为震惊,因为这些示例在现实世界中如此普遍,因此我每天都会在过程代码中使用这些类型的查询。
因此,假设您有一个User类和一个PhoneNumber类。一个用户可以有多个电话号码。好吧,这是一个完全正常的现实用例。您想获取所有User
和相关的PhoneNumber
。你将如何从PDO检索结果,实例化用户和填充每个用户private $phoneNumbers = [];
财产?我是新手OOP心中想说的foreach在一个关联的结果集,由USER_ID排序,然后遍历结果集的实例化一个新用户(),并设置其属性和遍历每个用户,实例化一个新的******中国(),并将其推到财产,但最肯定似乎“错误”。
当人们说这些类型的例子不是司空见惯(仍然感到震惊)时,我总是回到亚马逊/电子商务的例子。你有一个订单历史记录页面。那将有一个Order类和一个OrderItem类。您可以在单个页面上查看所有订单上所有Order
和OrderItem
的购买。
由于我不想因为没有任何代码而受到抨击,所以这绝对是不正确的,但这就是我试图理解实例化对象结果以及相关结果的“方法”。原始对象的
在程序我可以在一查询溶液做到这一点。我期望在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中的迭代。
答案 0 :(得分:0)
什么你试图避免对每个订单的订单项目的循环查询。
您想有疑问的恒定数量,而不是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,但是我发现这种事情是微优化,尤其是当您会遇到越来越多的关系。我尽量保持查询绑定到他们构建,而不是返回巨平的结果集多种不同的实体,除非有重大的性能需要涉及的对象。可以更容易地处理高速缓存以及