limetecbiotechnologies / yaml-fixtures-bundle
用于简化基于 YAML 的 fixtures 加载的 Symfony 包。
Requires
- doctrine/data-fixtures: 1.*
- doctrine/doctrine-bundle: ^2.6
- doctrine/doctrine-fixtures-bundle: 2.*|3.*
- doctrine/orm: 2.*
Requires (Dev)
- mockery/mockery: 0.9.*
- phpunit/phpunit: 5.*|^9.5
README
此包为您提供了使用基于 YAML 的 fixtures 为 Symfony2 和 Doctrine2 的方法。目前它与 Doctrine ORM 或 Doctrine MongoDB ODM 一起工作。其他后端尚未实现,但可以非常容易地实现。
安装
此包依赖于 DoctrineFixturesBundle。如果您还没有使用 Symfony2 配置它,请遵循 设置说明。
通过 Composer,在 composer.json 中添加以下内容
"require": {
"khepin/yaml-fixtures-bundle": "~1.0.1"
}
然后在 AppKernel.php
中注册该包,最好只在开发环境中注册它,因为它在其他地方不需要。
public function registerBundles()
{
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
//...
$bundles[] = new Khepin\YamlFixturesBundle\KhepinYamlFixturesBundle();
//...
}
}
配置
在您的 config.yml
或 config_dev.yml
(推荐)中添加以下内容
khepin_yaml_fixtures:
resources:
- MyOtherBundle/load_this_first
- MyBundle
- MyOtherBundle
在 'resources' 下是一个包含您希望加载 fixtures 的包的列表。fixtures 将按此顺序加载。
MyOtherBundle/load_this_first
语法表示 load_this_first.yml
将在当前包中的其他文件之前加载。这允许为加载 fixture 文件设置任何特定的顺序。
定义您的 fixture 文件
设置
请注意,与 Symfony 1.x 不同,加载 fixtures 的顺序是重要的。有两种方法可以操作此顺序
- 通过
config.yml
:指定哪些包的 fixtures 首先加载 - 通过文件名:每个包内部按字母顺序加载 fixture 文件
默认情况下,fixture 文件使用包层次结构:MyBundle/DataFixtures/somefixtures.yml
。
如果您想更改使用的层次结构 fixtures,请在配置中指定它
khepin_yaml_fixtures:
directory: Resources/fixtures
这将导致您的 fixture 文件使用包层次结构:MyBundle/Resources/fixtures/somefixtures.yml
。
定义
您只能在每个文件中定义一个类的 fixtures。在顶级配置 fixture 文件,并在 fixtures
键内定义。您可以通过在 fixtures
数组中提供名称来为稍后引用的 fixture 命名。
model: Name\Space\MyBundle\Entity\User
tags: [ test, dev, prod ] # optional parameter
save_in_reverse: false # optional parameter
persistence: orm (default)| mongodb # lets you choose if these fixtures should be saved through the orm or through mongodb.
fixtures:
michael:
name: Michael
phonenumber: 8765658
birthday: "1989-12-12"
您可以通过提供名称来使用对先前创建的 fixtures 的引用
model: Name\Space\MyBundle\Entity\Car
fixtures:
audi_a3:
owner: michael
since: "2010-12-12"
对于 MongoDB 的 reference many,在相应的键下包含您的引用列表
model: Name\Space\Bundle\Document\Car
persistence: mongodb
fixtures:
audi_a3:
owners:
- michael
- paul
- angella
您还可以为同一实体定义尽可能多的文件。当与上下文标签(见下文)一起使用时,这将非常有用。
## 与日期和时间一起工作
日期需要用引号括起来。日期传递给DateTime,所以任何可以用DateTime处理的字符串都可以在这里使用。这包括相对格式,如“-1天”,“下个月”,“1周前”。
model: Name\Space\MyBundle\Entity\Statistics
fixtures:
stat-1:
product: example.org
date: "2010-12-12" #dates NEED to be set inside quotes
Mongo嵌入式文档
在Mongo中可以使用嵌入式文档(目前只实现了embed_one)。只需像这样继续你的yaml文件即可
model: Name\Space\Bundle\Document\Article
persistence: mongodb
fixtures:
first_post:
title: Ouelkom to my niew blog!
content: I will update regularly!
author: # This defines an embedded document
name: khepin # this will be set on the embedded document
用法
从命令行
php app/console khepin:yamlfixtures:load <context>
关于上下文,除非有理由,否则不需要添加上下文。
注意:目前命令行一次只能加载一个上下文。
从其他地方
如果您需要从其他地方加载数据库固定值,比如...您的功能测试,以设置一个干净的数据库进行测试,您可以通过服务容器访问同一内容,并且有能够一起加载多个上下文的优势。
$container->get('khepin.yaml_loader')->loadFixtures('prod', 'test', ...);
关于上下文
有时在设置测试用例时,需要不同的配置。这正是上下文旨在解决的问题。
上下文等同于在固定文件下的tag键中设置的标签。您可以在固定文件上设置任意数量的标签。例如prod
,test
等...
如果您以这种方式定义固定值,那么从命令行调用
php app/console khepin:yamlfixtures:load prod
将加载您设置的任何固定值文件。
tags: [ ..., prod, ... ]
这样,您可以定义在测试或开发环境中加载的固定值,但在例如prod环境中不加载。
没有任何标签的固定值文件将始终被加载!这样,您可以在没有任何标签的文件中设置引导固定值,然后为每个目的设置特定于每个目的的固定值。
那么这个“save_in_reverse”是什么东西?
这个参数大多数时候可以省略。它只有在您有一个自引用表时才有用。例如,如果您有如下固定值
fixtures:
last_level:
next_level: none
name: Meet the boss
middle_level:
next_level: last_level
name: complete the quest
start_level:
next_level: middle_level
name: introduction
在这种情况下,我们需要在我们的固定值中首先放置last_level
,因为它是唯一一个不引用其他内容的。我们不能首先创建start_level
,因为它需要middle_level
已经存在等...
这个问题在于,当清除数据库时,ORMPurger()会按照ID顺序逐行遍历。因此,如果我们按这个顺序保存它们,last_level
应该是第一个被删除的,这将会因为它仍然被middle_level
引用而引起外键问题。
按相反顺序保存将按此顺序创建对象,以便正确设置引用,然后再按相反的顺序保存,以便在清除数据库时没有异常。
处理ORM一对一或多对多关联
如果您想向*To-Many关联传递一个已创建的对象数组,您可以首先允许您的setter在对象上接受一个普通的PHP数组(而不是只接受Doctrine\Common\ArrayCollection),然后定义您的YAML文件如下
fixtures:
car:
name: foo_bar
parts:
- part_one
- part_two
- part_three
当然,假设part_one、part_two和part_three是您在先前加载的文件中定义的对象。
YAML加载器将创建一个包含三个对象的普通PHP数组,并将其传递给您在此文件中定义的模型上的setParts()等。
构造函数
如果您想向构造函数传递参数
fixtures:
car:
__construct:
- Ford
- {type: reference , value: owner_ford}
- {type: datetime, value: "2012-01-01"}
class Car { /** * @param string $name * @param Owner $owner * @param DateTime $purchaseDate */ public function __construct($name, Owner $owner, \DateTime $purchaseDate, ) { ... } }
服务调用
某些实体在持久化之前需要由特殊服务来管理。例如,对于FOSUserBundle,正确的密码是由user_manager
设置的,而不是直接在用户类中设置。因此,在对象可以被持久化之前,我们需要请求这个服务将我们的域对象设置到正确的状态。服务调用就是这样声明的
model: My\NameSpace\User
service_calls:
service_1:
service: fos_user.user_manager # this is the service id in the container
method: updateUser # the method to be called on the object
fixtures:
dad:
name: Francois
plain_password: thisismypassword
现在,对于每个用户,在它被持久化之前,将发生类似以下代码的操作
$container->get('fos_user.user_manager')->updateUser($user_francois);
使用访问控制列表(ACL)
如果您需要在您的固定数据上设置ACL条目,这是可能的。ACL是在所有固定数据保存之后创建的,以确保没有可能的冲突。
要为固定数据设置ACL,您需要使用ProblematicAclManagerBundle。
并且按照以下方式更新您的配置
khepin_yaml_fixtures:
acl_manager: ~
resources:
- MyBundle
- MyOtherBundle
ACL只能使用Symfony MaskBuilder定义的标准掩码。例如
model: My\NameSpace\Car
tags: [ test ]
fixtures:
dad_car:
name: Mercedes
mom_car:
name: Mini Cooper
acl:
dad_car:
user_dad: MASK_OWNER
user_mom: MASK_MASTER
mom_car:
user_mom: MASK_OWNER
请注意,Symfony中的ACL不是通过Doctrine管理的,因此当您重新创建固定数据时,它们不会被清除。然而,如果有任何冲突,加载ACL将覆盖所有之前的ACL条目。