我对 Rails ActiveRecord、Doctrine for PHP(和类似的 ORM)背后的一些设计很感兴趣。
- ORM 如何实现链式访问器之类的功能以及它们通常工作的深度?
- ORM 如何在内部构造查询?
- ORM 如何管理查询,同时保持对它的期望的任意性?
显然这是一个学术问题,但欢迎所有性质的答案!
(我选择的语言是OO PHP5.3!)
我对 Rails ActiveRecord、Doctrine for PHP(和类似的 ORM)背后的一些设计很感兴趣。
显然这是一个学术问题,但欢迎所有性质的答案!
(我选择的语言是OO PHP5.3!)
链式方法调用与 ORM 问题是正交的,它们在 OOP 中随处可见。可链式方法只返回对当前对象的引用,允许调用返回值。在 PHP 中
class A {
public function b() {
...
return $this;
}
public function c($param) {
...
return $this;
}
}
$foo = new A();
$foo->b()->c('one');
// chaining is equivilant to
// $foo = $foo->b();
// $foo = $foo->c();
至于如何构造查询,有两种方法。在像 ORM 这样的 ActiveRecord 中,有一些代码可以检查数据库的元数据。大多数数据库都有某种 SQL 或类似 SQL 的命令来查看此元数据。(MySQL 的DESCRIBE TABLE
、Oracle 的USER_TAB_COLUMNS
表等)
一些 ORM 让您使用中性语言(例如 YAML)来描述您的数据库表。其他人可能会从您创建对象模型的方式中推断出数据库结构(我想说 Django 会这样做,但我已经有一段时间没有看过它了)。最后是一种混合方法,使用前两种技术中的任何一种,但提供了一个单独的工具来自动生成 YAML/etc。或类文件。
一个表的名称和数据类型是已知的,很容易实用地编写一个返回所有行或满足特定条件的特定行集的 SQL 查询。
至于你的最后一个问题,
ORM 如何管理查询,同时保持对它的期望的任意性?
我认为答案是“不太好”。一旦超越了单表、单对象的比喻,每个 ORM 都有不同的方法,即关于如何使用 SQL 查询来建模对象的理念。但在抽象中,它就像添加基于 ORM 假设构造查询的新方法一样简单(即 Zend_Db_Table 的“findManyToManyRowset”方法)
ORM 如何实现链式访问器之类的功能以及它们通常工作的深度?
似乎没有人回答这个问题。我可以快速描述 Doctrine 如何在 PHP 中做到这一点。
在 Doctrine 中,您在对象模型上看到的所有字段实际上都不是为该类定义的。因此,在您的示例 $car->owners 中,$car 的类中没有定义名为“所有者”的实际字段。
相反,ORM 使用__get 和 __set 之类的魔术方法。所以当你使用像 $car->color 这样的表达式时,PHP 内部会调用 Doctrine_Record#__get('color')。
此时,ORM 可以以任何必要的方式自由地满足这一点。这里有很多可能的设计。例如,它可以将这些值存储在一个名为 $_values 的数组中,然后返回 $this->_values['color']。特别是 Doctrine 不仅跟踪每条记录的值,而且还跟踪其相对于数据库中持久性的状态。
一个不直观的例子是 Doctrine 的关系。当您获得对 $car 的引用时,它与称为“所有者”的 People 表有关系。因此,$car->owners 的数据实际上存储在与 $car 本身的数据不同的表中。所以 ORM 有两种选择:
当然,Doctrine 使用 #2,因为对于任何具有中等复杂性的实际生产站点来说,#1 变得笨拙。但它也有副作用。如果您在 $car 上使用多个关系,则 Doctrine 将在您访问它时分别加载每个关系。因此,当可能只需要 1 个时,您最终会运行 5-6 个查询。
Doctrine 允许您通过使用 Doctrine 查询语言来优化这种情况。您告诉 DQL 您要加载汽车对象,但还要将其加入其所有者、制造商、所有权、留置权等,它会将所有这些数据加载到对象中。
哇!长响应。不过,基本上,您已经掌握了“ORM 的目的是什么?”的核心。和“我们为什么要使用一个?” ORM 允许我们在大多数情况下继续以对象模式思考,但抽象并不完美,抽象中的泄漏往往会导致性能损失。
我创建了一个关于构建 PHP DataMapper 主题的演示文稿,您可能会感兴趣。当我在那里为 PHP 用户组展示它时,它被记录在俄克拉荷马城联合办公协作组织的视频中:
视频: http ://blip.tv/file/2249586/
演示幻灯片: http ://www.slideshare.net/vlucas/building-data-mapper-php5-presentation
该演示文稿基本上是phpDataMapper的早期概念,尽管此后发生了很多变化。
希望它们能帮助您更好地理解 ORM 的内部工作原理。
链式访问器并不是什么大问题:你return $this
来自 setter 方法。繁荣,完成,在你喜欢的多个层次上工作。