此答案已被改写。
大多数数据映射器通过为每个类表示一个对象来工作,或者“模型”通常是创造的术语。如果您希望允许通过单个对象(即$model->find()
)进行多次访问,则通常会对其进行定义,以便该方法实际上不会返回其自身的实例,而是返回数组或MongoCursor
急切加载类到空间中的实例。
这种范式通常与“Active Record”相关联。这是 ORM、ODM 和框架都用来以一种或另一种方式与数据库通信的方法,不仅适用于 MongoDB,而且适用于 SQL 和任何其他碰巧出现的数据库(Cassandra、CouchDB 等)。
应该立即注意到,即使活动记录提供了很大的功能,也不应该在整个应用程序中覆盖它。有时直接使用驱动程序会更有益。由于这个原因,大多数 ORM、ODM 和框架都提供了快速轻松地直接访问驱动程序的能力。
正如许多人所说,没有轻量级数据映射器。如果您要将返回的数据映射到类,那么它将消耗资源,结束。这样做的好处是您在操作对象时获得的力量。
活动记录非常擅长从 PHP 中提供事件和触发器。一个很好的例子是我为 Yii 制作的 ORM:https ://github.com/Sammaye/MongoYii它可以提供以下钩子:
afterConstruct
beforeFind
afterFind
beforeValidate
afterValidate
beforeSave
afterSave
应该注意的是,当涉及到事件时beforeSave
,afterSave
MongoDB 不具备触发器 ( https://jira.mongodb.org/browse/SERVER-124 ),因此应用程序应该处理它是有道理的。除了应用程序处理此问题的明显原因之外,它还可以通过调用您的本地 PHP 函数来处理在访问数据库之前保存的每个文档,从而更好地处理保存函数。
大多数数据映射器也使用 PHP 自己的类 CRUD 来表示它们的工作。例如创建一条新记录:
$d=new User();
$d->username='sammaye';
$d->save();
这是一个很好的方法,因为您创建了一个“新”(https://github.com/Sammaye/MongoYii/blob/master/EMongoDocument.php#L46展示了我如何为 MongoYii 中的新记录做准备)类来制作一个“新纪录。它在语义上非常适合。
更新函数通常通过读取函数访问,您无法更新您不知道存在的模型。这使我们进入了填充模型的下一步。
为了处理填充模型,不同的 ORM、ODM 和框架采用不同的方法。例如,我的 MongoYii 扩展使用model
在每个类中调用的工厂方法来带回自身的新实例,以便我可以调用动态find
和findOne
其他此类方法。
一些 ORM、ODM 和框架提供读取函数作为直接static
函数,使它们本身成为工厂方法,而有些使用单例模式,但是,我选择不这样做(https://stackoverflow.com/a/4596323/383478)。
大多数(如果不是全部)实现某种形式的游标。这用于返回多个模型并直接包装(通常)以返回预填充模型MongoCursor
来替换方法。current()
例如调用:
User::model()->find();
将返回一个EMongoCursor
(在 MongoYii 中),这将消除该类User
用于实例化光标的事实,并且在调用时如下所示:
foreach(User::model() as $k=>$v){
var_dump($v);
}
将在此处调用该current()
方法:https ://github.com/Sammaye/MongoYii/blob/master/EMongoCursor.php#L102返回模型的新单个实例。
有一些 ORM、ODM 和框架实现了急切的数组加载。这意味着他们只会将整个结果作为模型数组直接加载到您的 RAM 中。我个人不喜欢这种方法,因为在需要添加到旧记录的地方添加了一些新功能,所以当您需要使用活动记录进行更大的更新时,这很浪费而且也不是好兆头。
在我继续之前的最后一个主题是 MongoDB 的无模式特性。将 PHP 类与 MongoDB 一起使用的问题在于,您需要 PHP 的所有功能,但又需要 MongoDB 的可变特性。这在 SQL 中很容易克服,因为它具有预定义的模式,您只需查询它并完成工作;但是,MongoDB 没有这样的东西。
这确实使 MongoDB 中的模式处理非常危险。大多数 ORM、ODM 和框架都要求您使用带有和方法的private
变量在现场(即 Doctrine 2)预先定义模式。在 MongoYii 中,为了让我的生活变得轻松优雅,我决定通过使用可以检测到的魔法来保留 MongoDB 的无模式特性(https://github.com/Sammaye/MongoYii/blob/master/EMongoModel.php#L26是我的和https ://github.com/Sammaye/MongoYii/blob/master/EMongoModel.php#L47是我的),如果该属性在类中无法访问,如果该字段在内部数组中,如果不是,则返回。同样,为了设置属性,我只需在内部变量中设置。get
set
__get
__set
_attributes
null
_attributes
至于如何分配此架构,我将内部分配留给用户,但是为了处理表单等设置属性,我使用了验证规则(https://github.com/Sammaye/MongoYii/blob/master/EMongoModel. php#L236)调用一个被调用的函数,该函数getSafeAttributeNames()
将返回一个属性列表,该列表具有针对它们的验证规则。如果它们没有验证规则,则不会设置传入$_POST
或数组中存在的那些属性。$_GET
因此,这提供了架构但安全的模型结构的能力。
所以我们已经介绍了如何实际使用根文档,您还询问数据映射器如何处理子文档。Doctrine 2 和许多其他提供了完整的基于类的子文档(http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/embedded-mapping.html),但这可能非常足智多谋。相反,我决定提供帮助函数,以允许灵活使用子文档,而无需急切地将它们加载到模型中并因此消耗 RAM。基本上我所做的就是让他们离开他们,因为他们是一个提供验证者(https://github.com/Sammaye/MongoYii/blob/master/validators/ESubdocumentValidator.php) 用于验证它们的内部。当然,验证器是自生成的,因此如果您在验证器中有一条规则再次使用验证器来发出嵌套子文档的验证,那么它将起作用。
所以我认为这完成了对 ORM、ODM 和框架使用数据映射器的非常基本的讨论。当然,我可能可以就此写一篇完整的文章,但我相信这是一个足够好的讨论。