归纳问题:
构建我的 class T
的最佳实践是什么?对象,当我从 MongoCursor::getNext()
收到它时?就目前而言,getNext()
MongoCursor
的函数返回 array
.我希望使用该点的结果作为 object
类型 T
.
我应该为类型 T
编写自己的构造函数吗? , 接受 array
?是否有任何通用的解决方案,例如当输入 T extends G
时, 和 G
以常规方式递归地完成这项工作(对于嵌套文档)。
我是 MongoDB 的新手,我想用一个漂亮的界面构建我自己的通用映射器。
赏金:
最佳答案
这个答案已被重写。
大多数数据映射器通过为每个类表示一个对象来工作,或者“模型”通常是创造出来的术语。如果您希望允许通过单个对象进行多次访问(即 $model->find()
),通常会删除该方法,以便该方法实际上不会返回自身的实例,而是返回数组或 MongoCursor
的实例。急切地将类加载到空间中。
这种范式通常与“Active Record”有关。这是 ORM、ODM 和框架都用来以一种或另一种方式与数据库通信的方法,不仅适用于 MongoDB,还适用于 SQL 和任何其他出现的数据库(Cassandra、CouchDB 等)。
应该立即注意到,即使事件记录提供了很大的力量,它也不应该覆盖整个应用程序。有时直接使用驱动程序会更有好处。出于这个原因,大多数 ORM、ODM 和框架都提供了快速、轻松地直接访问驱动程序的能力。
正如许多人所说,没有轻量级数据映射器。如果您要将返回的数据映射到类,那么它将消耗资源,结束。这样做的好处是您在操作对象时获得的力量。
Active Record 非常擅长从 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 ),所以应用程序应该处理这个是有道理的。除了应用程序处理此问题的明显原因之外,它还可以通过调用您的 native PHP 函数来操作在接触数据库之前保存的每个文档,从而更好地处理保存函数。大多数数据映射器也使用 PHP 自己的类 CRUD 来表示它们的。例如创建一个新记录:
$d=new User();
$d->username='sammaye';
$d->save();
这是一个很好的方法,因为您创建了一个"new"( https://github.com/Sammaye/MongoYii/blob/master/EMongoDocument.php#L46 展示了我如何准备在 MongoYii 中创建新记录)类来创建"new"记录。它在语义上非常适合。
更新函数通常通过读取函数访问,您无法更新您不知道存在的模型。这使我们进入了填充模型的下一步。
为了处理填充模型的不同 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 和框架可以实现 Eager 数组加载。这意味着他们只会将整个结果作为模型数组直接加载到您的 RAM 中。我个人不喜欢这种方法,因为在需要添加到旧记录的地方添加了一些新功能,所以当您需要使用事件记录进行较大更新时,它既浪费又不是好兆头。
在我继续之前的最后一个话题是 MongoDB 的无模式特性。将 PHP 类与 MongoDB 一起使用的问题在于,您需要 PHP 的所有功能,但具有 MongoDB 的可变性质。这在 SQL 中很容易克服,因为它有一个预定义的模式,你只需查询它并完成工作;但是,MongoDB 没有这样的东西。
这确实使 MongoDB 中的模式处理非常危险。大多数 ORM、ODM 和框架要求您使用
private
在现场(即 Doctrine 2)预先定义模式带有 get
的变量和 set
方法。在 MongoYii 中,为了让我的生活轻松优雅,我决定通过使用魔术来保留 MongoDB 的无模式性质( https://github.com/Sammaye/MongoYii/blob/master/EMongoModel.php#L26 是我的 __get
和 https://github.com/Sammaye/MongoYii/blob/master/EMongoModel.php#L47 是我的 __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 和框架使用数据映射器的非常基本的讨论。当然,我可能会就此写一整篇文章,但就我认为的这一分钟而言,这是一个足够好的讨论。
关于php - MongoDB 对象映射 (PHP),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16716594/