MongoDB对象映射(PHP)

时间:2013-05-23 14:28:08

标签: php mongodb object mapping

引导问题:

当我从class T收到MongoCursor::getNext()对象时,构建我的getNext()对象的最佳做法是什么?就目前而言,MongoCursor的{​​{1}}函数返回array。我希望将该点的结果用作object类型的T

我应该为T类型编写自己的构造函数,它接受array吗?是否存在任何通用解决方案,例如,当类型T extends GG以常规方式执行作业时,递归地(对于嵌套文档)。

我是MongoDB的新手,我想用一个漂亮的界面构建我自己的通用映射器。

奖励:

  • 从PHP的角度来看,哪种方法,模式最适合MongoDB的概念。

1 个答案:

答案 0 :(得分:3)

这个答案已被重写。

大多数数据映射器通过表示每个类的一个对象或"模型"通常是创造的术语。如果您希望允许通过单个对象进行多次加入(即$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

应该注意的是,当涉及beforeSaveafterSave之类的事件时,MongoDB不具有触发器(https://jira.mongodb.org/browse/SERVER-124),因此应用程序应该处理这个问题。除了应用程序处理这个问题的明显原因之外,它还可以通过调用本机PHP函数来处理保存函数,以便在触摸数据库之前操作保存的每个文档。

大多数数据映射器都使用PHP自己的类CRUD来表示它们。例如,要创建新记录:

$d=new User();
$d->username='sammaye';
$d->save();

这是一个非常好的方法,因为你创建了一个" new" (https://github.com/Sammaye/MongoYii/blob/master/EMongoDocument.php#L46显示我如何为MongoYii)课程中的新记录做准备以制作一个新的"记录。它在语义上很合适。

更新功能通常通过读取功能访问,您无法更新您不了解其中的模型。这将我们带入填充模型的下一步。

为了处理模型的不同ORM,ODM和框架提交不同的方法。例如,我的MongoYii扩展在每个类中使用一个名为model的工厂方法来返回一个新的自身实例,这样我就可以调用动态findfindOne以及其他类似的方法。 / p>

一些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和框架要求您使用带有privateget方法的set变量预先定义现场(即Doctrine 2)中的模式。在MongoYii中,为了让我的生活变得轻松优雅,我决定通过使用可以检测到的魔法(https://github.com/Sammaye/MongoYii/blob/master/EMongoModel.php#L26是我的__gethttps://github.com/Sammaye/MongoYii/blob/master/EMongoModel.php#L47是我的__set来保留MongoDBs的无模式性质。 ),如果属性在类中无法访问,如果该字段位于内部_attributes数组中,如果不存在则返回null。同样,对于设置属性,我只需在intrernal _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和框架的基本讨论,使用了数据映射器。当然,我可能会写一篇关于这篇文章的全文,但对于我认为的那一刻,这是一个很好的讨论。