trismegiste / dokudoki
无模式数据库层用于MongoDB和无模式功能的辅助工具
Requires
- php: >=5.5
- ext-mongo: >=1.5.0
- trismegiste/yuurei: 2.3.*
Requires (Dev)
- nikic/php-parser: 0.9.x-dev
- symfony/config: 2.3.*
- symfony/console: 2.3.*
- symfony/dependency-injection: 2.3.*
- symfony/form: 2.3.*
- symfony/http-kernel: 2.3.*
- symfony/yaml: 2.3.*
README
是什么
这是一个基于 Yuurei 的扩展包,Yuurei 是一个具有自动映射的微型数据库层。
除了 Yuurei 的原始映射外,该扩展包还添加了多个功能:3 种其他映射系统(完全魔法、别名和两者的混合)、处理 MongoDate 和上传文件的表单组件、用于 WebProfiler 的 DataCollector,以及当然,许多注入到 symfony2 的 DiC 中的服务。
我试图创建一个无差别的 DBAL,它能够在模型完成与否的情况下帮助您构建应用程序。因此,这里也有迁移工具。
如何使用
像其他 PHP 包一样使用 Composer
对于 Symfony 2.3
"require": {
"trismegiste/dokudoki": "dev-master"
},
Symfony 2.x 的遗留版本
"require": {
"trismegiste/dokudoki": "dev-Symfony2.x"
},
为什么
因为,就像蛋糕一样,“ODM 是一个谎言”。将 MongoDB 转换成类似 ORM 的抽象是您可以对 NoSQL 数据库做的最糟糕的事情。
我对 CRUD 反模式和 ORM 和代码生成器产生的贫血模型感到厌倦。最终,您意识到您必须为 ORM 而不是为业务建模您的类。我想要恢复在 Symfony2 中发展的领域驱动开发哲学,而不是 Doctrine2 破坏的哲学。
指导
- 丰富的文档 哇!您具有原子性。
- 停止思考 1 实体 <=> 1 表
- 只有少数根实体:2、3 或 4,对于完整的电子商务应用程序,最多 10 个!
- 1 应用 <=> 1 集合
- 忘记 1NF、2NF 和 3NF。在 MongoDB 中处理反规范化数据比在 MySQL 中处理过多的 LEFT JOIN 更容易。
- 像 serialize/unserialize 一样思考
- 不要尝试用您的数据库来复制搜索引擎:使用 Elastic Search
- 不要尝试在集合中存储所有内容:使用 XML 文件
因此,您将模型分成几个部分,没有循环引用,并将其存储。这就像序列化,但是在 MongoDB 中。
所有非静态属性都以一种方式存储,您可以使用 MongoDB 强大的(但坦白说很奇怪)语言轻松地进行查询。
查看 PHPUnit 测试 以获取示例。
四种工作模式
此 DBAL 有 4 个阶段,根据模型类的完成程度而定。
技巧在于,在开发您的应用程序时,可以在这些阶段之间迁移,例如,在有了脏原型之后不需要从头开始。甚至在没有模型的情况下,您还可以从已存储在集合中的数据生成模型。
黑魔法是黑色的
如果您没有模型并且要设计许多表单,请从“黑魔法”阶段开始。如果您对贫血模型就足够了,就无需创建它。它充满了魔法方法、魔法映射和魔法文档,就像在工作原型。
但请注意:它是用于原型设计的,如果您不小心,数据库可能会反击您。这就是我所说的“表单驱动开发”。
请参阅单元测试中的完整示例
// construct a form $form = $this->formFactory ->createBuilder('magic_form', null, array('class_key' => 'product')) ->add('title') ->add('price') ->getForm(); // bind data to the form $form->bind(array('title' => 'EF-85 L', 'price' => 2000)); $doc = $form->getData(); // getting the magic document $this->assertInstanceOf('Trismegiste\DokudokiBundle\Magic\Document', $doc); $this->assertEquals('product', $doc->getClassName()); $this->assertEquals('EF-85 L', $doc->getTitle()); // persistence with blackmagic repository $this->blackmagic->persist($doc); // restoring with blackmagic repository $restore = $this->blackmagic->findByPk((string) $doc->getId()); $this->assertInstanceOf('Trismegiste\DokudokiBundle\Magic\Document', $restore); $this->assertEquals('product', $restore->getClassName()); $this->assertEquals('EF-85 L', $restore->getTitle());
序列化可能足够
这是来自Yuurei的原始映射系统。如果您有很多几乎完成模型的类并且不想配置任何内容,请使用“调用”阶段。只有魔法映射和对象与文档之间的严格类型。
但如果您需要执行复杂查询或映射-减少,这可能会非常脏。此阶段对没有GUI的RESTful应用程序非常有用。
请参阅单元测试中的完整示例
// simple object $doc = new \Some\Sample\Product('EF-85 L', 2000); // persisting $this->invocation->persist($doc); // restoring with invocation repository $restore = $this->invocation->findByPk((string) $doc->getId()); $this->assertInstanceOf('Some\Sample\Product', $restore); // retrieving the content in the MongoDB $dump = $this->collection->findOne(array('_id' => $doc->getId())); $this->assertEquals('Some\Sample\Product', $dump['-fqcn']); // we store the FQCN $this->assertEquals('EF-85 L', $dump['title']); $this->assertEquals(2000, $dump['price']);
白魔法是为正义而存在的
如果您有一个好的模型并且有时间在数据库中仔细别名类,请使用“白魔法”阶段。有自动映射,但不用说,您的模型不能变成混乱。任何未别名的类都会引发异常。
请参阅单元测试中的完整示例
// simple object $doc = new \Some\Sample\Product('EF-85 L', 2000); // persisting $this->whitemagic->persist($doc); // restoring with whitemagic repository $restore = $this->whitemagic->findByPk((string) $doc->getId()); $this->assertInstanceOf('Some\Sample\Product', $restore); // retrieving the content in the MongoDB $dump = $this->collection->findOne(array('_id' => $doc->getId())); $this->assertEquals('product', $dump['-class']); // here is the aliasing $this->assertEquals('EF-85 L', $dump['title']); $this->assertEquals(2000, $dump['price']);
Hoodoo 子类
如果您需要进化上述模型,可以使用“Hoodoo”阶段,这是一个“白魔法”阶段,混合了一些来自“黑魔法”阶段的魔法。有一个安全网来防止一些“真实”的类变成“虚假”的类。这降低了每分钟的WTF率,您可以选择魔法级别。
关于性能
我还没有完全测试这个dbal,但是填充是这个层中最慢的部分。例如,一个包含100个实体的文档(考虑一个客户,他的地址,过去的订单和产品等...)在没有APC的标准双核台式机上存储需要大约100毫秒。
它并不非常高效,但当您寻求高性能时,您可能会忘记ORM、ODM等。
常见问题解答
是否必须使用Symfony2?
不,DBAL可以以独立模式工作。无论如何,您会错过许多表单和黑魔法阶段的特性。完整的框架本身不是必需的,只有一些组件。而且几乎所有的单元测试都不需要任何symfony组件。例如,您可以为silex和pimple轻松创建一个提供者。
有什么要求?
- PHP >= 5.4
- PECL Mongo扩展 >= 1.3
- dev-master版本使用Symfony2.3运行,并且有2.1和2.2的分支。
如何映射属性?
所有对象的属性都将被存储。你只需做一件事:根类必须实现Persistable接口(有一个用于实现此接口的特质)。你不需要扩展任何特定的类,因此你可以无约束地遵循DDD。
什么是“根类”?
这是存储在集合中的一个类,它在键'_id'中包含MongoId。此类中所有其他聚合对象不需要实现Persistable,它们将被递归存储。
我如何移除一些瞬态属性?
你无法移除。但你可以有一个实现了Skippable接口的瞬态类。使用装饰器模式或状态模式。你的模型可以做到这一点。
在持久化前我可以做一些清理吗?
像序列化一样,你可以通过实现Cleanable接口的2个方法:wakeup和sleep来实现。
我如何进行查询列表?
使用MongoCollection,你无法比这个底层层更高效。
我如何存储图片或PDF?
在你的模型中使用MongoBinData,它将按原样存储。
我可以使用MongoId之外的其它东西作为主键吗?
不可以。
关于MongoDate呢?
所有DateTime都将转换为MongoDate,反之亦然。
我看到你在你的类模型中使用mongo类型,关于抽象呢?
严肃地说,你有没有将一个应用程序切换到另一个数据库?
对于DBRef有延迟加载或代理类吗?
Fly,你们这些傻瓜。
为什么这个名字这么傻?
嗯,我不擅长找名字,这就是我倾向于保留最荒谬而不是最严肃的原因。它代表Docu(ment) + doki,为了回忆“dokidoki”(在日本语中大致意味着“兴奋”,听起来像心跳)。这个,我相当确信它是独一无二的 ^_^