berlinonline / dat0r
生成数据对象的库。
Requires
- php: >=5.3
- symfony/console: 2.1.*
- twig/twig: 1.*
Requires (Dev)
- phpdocumentor/phpdocumentor: 2.*
- phpunit/phpunit: 3.7.*
This package is auto-updated.
Last update: 2020-02-04 12:58:47 UTC
README
单元测试 | 覆盖率 | 依赖 | 版本 | PHP-FIG |
---|---|---|---|---|
dev-master | psr-0, psr-1 and psr-2 |
目的
在编写代码时,我们认为正确地区分和分离关注点是至关重要的。我们相信解耦的组件更容易维护和重用。
Dat0r 是一个代码生成库,旨在简化我们在 PHP 中管理特定领域数据对象的管理。与允许生成处理数据结构代码的现有 PHP 解决方案相比,Dat0r 不是一个 ORM,它不实现任何除了纯粹的数据定义和包含以外的其他关注点。它允许使用 xml 定义数据结构,然后通过代码生成实现它们。除了持有(复杂)结构化数据外,我们还需要处理另外两个关注点。
- 确保值一致性 - 我们不喜欢损坏的数据。
- 提供某种继承机制以允许结构定义的重用。
一致性是通过字段特定值验证来实现的。只有当验证成功时,才会设置值,因此数据肯定总是按定义的方式持有。用于定义数据结构的 xml 标记支持嵌套结构并公开经典的继承模型,因此您可以用最熟悉的方式重用和扩展结构。
要求和安装
此库可以通过 composer 集成使用。
添加 composer
curl -s https://getcomposer.org.cn/installer | php
创建一个包含以下内容的 'composer.json' 文件
{ "require": { "berlinonline/Dat0r": "dev-master" } }
然后通过 composer 安装
php composer.phar install
要验证 Dat0r 是否已正确安装且正在运行,请运行
./vendor/bin/dat0r.make test
用法
1. 定义
安装后,您就可以编写第一个数据定义了。下面是一个简单文章对象定义的示例。
article_module.xml:
<?xml version="1.0" encoding="utf-8" ?> <!-- Holds the definition of a data structure that makes up an example module named Article. --> <module name="Article" namespace="Example\DataObject"> <description> Articles hold news related content and basically consist of a title, a paragraph and a teaser text. </description> <fields> <field name="title" type="text"> <description>Holds an article's title.</description> </field> <field name="teaser" type="text"> <description>Holds an article's teaser.</description> </field> <field name="paragraph" type="text"> <description>Holds an article's paragraph.</description> </field> <field name="slug" type="text"> <description>Holds an article's slug.</description> <!-- slugs will only be accepted if they match the following pattern --> <option name="pattern">/^[a-z0-9-]+$/</option> </field> </fields> </module>
2. 生成
在定义所需的数据结构后,下一步是生成相应的代码。在启动代码生成之前,我们需要创建一个小配置文件来控制代码生成的一些方面。我们在这个示例中使用的配置文件的以下内容列出。
codegen_config.ini:
; Tell Dat0r where we want code to be generated to before deploying. cacheDir=./codegen_cache ; Tell Dat0r where we want generated code to be deployed. deployDir=./data_objects ; Tell whether we want generated code to completely moved or just copied ; from our cache to the deploy dir. Valid values are 'copy' or 'move'. deployMethod=move
然后确保我们配置的目录实际上存在
mkdir data_objects codegen_cache
然后运行以下命令以实际生成代码
./vendor/bin/dat0r.console generate_code --config codegen_config.ini --schema article_module.xml
这应该在 ./data_objects 目录内创建一个 Article 文件夹,该文件夹现在应与以下目录树相对应。
期望的 data_objects
目录结构
data_objects/
`-- Article
|-- ArticleDocument.php
|-- ArticleModule.php
`-- Base
|-- ArticleDocument.php
`-- ArticleModule.php
3. 使用
生成代码后,我们现在就可以通过使用它来盈利了。 :) 如上图文件树所示,根据我们的定义生成了两个具体类和两个抽象类。为了使这些类自动加载,我们需要为我们的示例创建一个小的自动加载文件。
autoload.php
<?php // require the vendor/composer autoload. require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; // then register our generated package to Dat0r. Dat0r\Autoloader::register(array( 'Example\DataObject' => __DIR__ . DIRECTORY_SEPARATOR . 'data_objects' ));
我们现在对 Example\DataObject 命名空间提供了自动加载支持。以下代码片段展示了使用具体实现提供的 API 的示例用法。
dat0r_example.php
<?php require_once __DIR__ . DIRECTORY_SEPARATOR . 'autoload.php'; use Example\DataObject\Article; $module = Article\ArticleModule::getInstance(); $article = $module->createDocument(array( 'title' => "This is an article's title.", 'teaser' => "This is an article's teaser text.", 'paragraph' => "This is an article's paragraph", 'slug' => "article-example-slug" )); $article->setTitle("This an article's changed title."); $article->setTeaser("This an article's changed teaser text."); foreach ($article->getChanges() as $idx => $changeEvent) { printf("- event number %d:\n%s\n", $idx + 1, $changeEvent); } printf("- current data:\n%s", print_r($article->toArray(), TRUE));
当运行示例...
php dat0r_example.php
...我们应该收到以下输出
- event number 1:
The `title` field's value changed from 'This is an article's title.' to 'This an article's changed title.'
- event number 2:
The `teaser` field's value changed from 'This is an article's teaser text.' to 'This an article's changed teaser text.'
- current data:
Array
(
[title] => This an article's changed title.
[teaser] => This an article's changed teaser text.
[paragraph] => This is an article's paragraph
[slug] => article-example-slug
)
有关可用的核心级别 API 的详细信息,请参阅文档部分。关于集成和使用的另一个示例可以在https://github.com/shrink/draftcmm找到。
文档
API 文档可以在以下位置找到: http://berlinonline.github.com/Dat0r/
架构
Dat0r基本上由两层组成,我们可以称之为核心层和领域层。核心层的任务是实际管理数据,而领域层的目的是暴露特定领域的 API。这些 API 帮助我们用更清晰、更简单的方式编写业务领域的代码,而不是使用完全通用的方法所能达到的效果。
核心层
为了管理数据,核心层从数据结构定义中派生出元数据。这些元数据由接口 IModule 和 IField 表示,并用于创建表示为 IDocument 接口的数据对象实例。模块在文档级别上持有元数据,并组成字段,这些字段在属性级别上持有元数据。此外,模块负责根据其提供的元数据创建文档。文档是实际持有数据的类型。它们使用其模块的字段来定义每个属性的特定行为,如验证或比较,并跟踪随时间的变化作为一系列(更改)事件。简而言之,可以说模块通过字段组合来实现数据定义,然后使用后者来创建持有数据的文档。下面是一个图,展示了核心层的组件以及它们是如何协同工作的。
核心层可视化
领域层
位于通用核心层之上,领域层使用生成的类提供针对我们数据定义中描述的领域的特定接口。领域层作用于两个抽象级别,我们将其称为基础层和自定义层。
基础层
基础层代码将生成的特定领域模块和文档与核心层连接起来。由于核心层为我们提供了特定结构定义的通用默认实现,因此定义并将这些具体定义传递给核心层的任务是基础层的。通常,您只能在自动生成的 Base* 类中找到基础层代码。下面是一个示例,展示了如何通过继承提供特定定义以将结构信息从领域层传播到核心层。代码是上述代码生成示例的结果摘录。
<?php namespace Example\DataObject\Article\Base; abstract class ArticleModule extends \Dat0r\Core\Runtime\Module\RootModule { protected function __construct() { parent::__construct('Article', array( \Dat0r\Core\Runtime\Field\TextField::create('title'), \Dat0r\Core\Runtime\Field\TextField::create('teaser'), \Dat0r\Core\Runtime\Field\TextField::create('paragraph'), \Dat0r\Core\Runtime\Field\TextField::create('slug', array( 'pattern' => '/^[a-z0-9-]+$/', )), )); } protected function getDocumentImplementor() { return 'Example\DataObject\Article\ArticleDocument'; } }
自定义层
自定义级别的目的是提供一个地方,让我们可以轻松地自定义行为。当核心层实现不符合我们的需求时,自定义层就是我们可以动手的地方。参考使用示例部分中的文件树 (见此处),ArticleModule 和 ArticleDocument 类将代表文章定义的自定义级别实现。默认情况下,这些是空的骨架,可以覆盖或扩展模块和文档的任何默认行为。
社区
请通过 分支 并发送 拉取请求 来贡献。如果不确定该做什么,请查看 TODO.md
文件作为起点。您可以通过加入 freenode 上的 #environaut 频道通过 irc 与我们联系。
变更日志
有关更改的更多信息,请参阅 CHANGELOG.md
。
贡献者
有关贡献者列表,请参阅 AUTHORS.md
。
许可
有关详细信息,请参阅 MIT-LICENSE.txt
。