因为没有答案,我将尝试描述我是如何解决这个问题的。这是一个玩具解决方案。
对数据库的查询是用一种我称为 QQL 的小语言完成的,这是受到 Doctrine 2 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),然后为每一行 Hydrate 每一个:
// 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
}
}
我的水合物方法非常薄(快速),因为许多构建的对象将被复制并被删除。从我的问题中的结果集中,您可以看到 User('Jeff') 将被构建 5 次,其中 4 次是重复的,将被丢弃。
读取结果后,有 3 个对象列表。用户、订单和付款。这些与解析树一起传递给 Graph Builder。
Graph Builder 使用解析树来查看关系。从“根”模型(由“FROM 用户”确定)开始,检查已解析的 QQL 以找到请求的 JOIN(User->id TO Order->user_id)将 Orders 添加到 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 和一个 Graph Builder。ORM 类是迄今为止最大的类,有近 200 行。
结论。有用。感觉类似于 Doctrine 2,(所以我最终过渡到使用 Doctrine 2 的痛苦会少一些)。它可能更有效。分析读取几千个对象的测试页面的结果,到目前为止,最慢的部分是 SQL 查询(3ms)和释放 mysqli 结果(1ms),包括模型类(0.7ms)。写它很有趣,只花了一天时间。