由于没有答案,我将尝试描述我是如何解决这个问题的。这只是一个玩具解决方案。
对数据库的查询使用了一种我称之为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)。编写这个项目很有趣,只用了一天。