PHP ORM如何将查询结果映射到对象中(水合化)

3

如果我运行类似于以下查询:

SELECT * FROM user u
LEFT JOIN orders o ON o.user_id=u.id
LEFT JOIN payments p ON p.order_id=o.id

导致数据如下所示:
u.id, u.name, o.id, o.item, o.date_time, p.id, p.amount, p.date_time
  99    jeff    17  spring   12-12-2012    12     10.99   13-12-2012
  99    jeff    18     jam   12-12-2012    13       .99   16-12-2012
  99    jeff    19     car   22-12-2012    14      1000   17-12-2012
  99    jeff    19     car   22-12-2012    15      1000   18-12-2012
  99    jeff    19     car   22-12-2012    16      1000   19-12-2012

用户Jeff有3个订单,他已经向他的汽车支付了3次款项。

给定PHP对象User,Order和Payment以及单个DB查询-如何进行数据填充?我希望看到伪代码、实际的php代码或者应该阅读的设计模式名称 :)

2个回答

3
由于没有答案,我将尝试描述我是如何解决这个问题的。这只是一个玩具解决方案。
对数据库的查询使用了一种我称之为QQL的小语言,这受到了Doctrine 2 DQL的启发。请参考DQL
SELECT *
FROM user
JOIN user order
JOIN order payment
WHERE user.name=?

这将被解析并生成SQL语句。解析树还用于将结果集映射回对象图。

每个模型都有与其他模型的“关系”描述。因此,用户具有一对多到订单,订单具有一对多到支付。该描述在一个包含模型名称、主键和外键名称的数组中。

要构建JOIN:

JOIN user order

检查User模型,获取表名为'users',然后查找关系'order',获取Order表名为'orders'和连接键。使用此信息构建:
JOIN orders o ON o.user_id=users.id

一旦查询运行并返回结果以构建对象图表。我所做的是获取查询中使用的所有不同模型(在此示例中为User、Order和Payment),然后对每一行进行填充:

// query DB and get results into an array called $rows
foreach ($rows as $row) {
    foreach (array('User', 'Order', 'Payment') as $model) {
        $o = new $model;
        $o->hydrate($row);
        // inspect primary key - have we got this object already? store or throw away
    }
 }

我的hydrate方法非常简洁(快速),因为构建的许多对象将是重复的并且会被删除。从问题的结果集中,您可以看到User('Jeff')将被构建5次,其中4个是重复的并将被丢弃。
一旦结果已读取,就会有3个对象列表。用户,订单和付款。这些与解析树一起传递给图形构建器。
图形构建器使用解析树查找关系。从“root”模型开始(由“FROM user”确定),检查解析的QQL以找到所请求的JOIN(User -> id TO Order -> user_id),将订单添加到User -> orders数组中。然后它使用(Order -> id TO Payment -> order_id)进行相同的操作。
结果是:
$user->name == 'jeff'
$user->orders[0]->item == 'spring'
$user->orders[1]->item == 'jam'
$user->orders[2]->item == 'car'
$user->orders[2]->payments[2]->date_time == '19-12-2012'

我最终有四个主要的类,ORM、一个所有模型都继承的Model_Base(这确保了每个模型都有“表名”、“列”和“关系”),一个QQLParser和一个图形构建器。ORM类是最大的,近200行。

总结. 它可以工作。它感觉很像Doctrine 2,(所以我最终转换到使用Doctrine 2将更少痛苦)。它可能更加高效。在测试页面读取几千个对象的结果中,最慢的部分是SQL查询(3ms)和释放mysqli结果(1ms),包括模型类(0.7ms)。编写这个项目很有趣,只用了一天。


0

PDO 允许自动填充对象,例如 PDOStatement::fetchObject


是的,但它不会创建一个对象树,例如在数据注入后,我期望($ jeff->orders [2]->payments [0]->amount === 1000) - we-all-fall-down
在这种情况下,ORM 要么手动从结果中构建对象($jeff = new User; $jeff->orders[] = new Order; $jeff->order[0]->total = $res['total']),要么生成字符串并反序列化它,或者类 A 拥有一个“神奇”的方法 __get,第一次访问 $jeff->orders 时使用 PDO 发出请求。(也可以是两者的组合) - Mathieu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接