铸造

版本: 当前的
  • 维护版本
编辑这个页面

铸造

安装

1
作曲家需要zenstruck/foundry -dev

使用: *来自这个bundle的命令,确保ob娱乐下载Symfony MakerBundle安装。

如果不使用Symfony ob娱乐下载Flex,请确保在您的测试/dev环境。

与这些文档中使用的实体相同

对于文档的其余部分,将使用以下示例实体:欧宝官网下载app

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
名称空间应用程序实体使用学说ORM映射作为ORM/ * * *@ORM实体(repositoryClass = \ App \ Repository \ CategoryRepository) * /类别/ * * *@ORM\ID() *@ORM* \ GeneratedValue ()@ORM\列(类型=“整数”)* /私人id/ * * *@ORM\列(类型=“字符串”,长度= 255)* /私人的名字公共作用__construct(字符串的名字这个->名称=的名字; }/ /……getter / setter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
名称空间应用程序实体使用学说ORM映射作为ORM/ * * *@ORM\实体(repositoryClass =“app \ repository \ postrepository”)* /帖子/ * * *@ORM\ID() *@ORM* \ GeneratedValue ()@ORM\列(类型=“整数”)* /私人id/ * * *@ORM\列(类型=“字符串”,长度= 255)* /私人标题/ * * *@ORM\列(type = " text ",可空= true) * /私人身体/ * * *@ORM\列(type=“datetime”)*/私人createdAt/ * * *@ORM\列(type = " datetime, nullable = true) * /私人publishedAt/ * * *@ORM\ ManyToOne (targetEntity =类别::类)*@ORM\ JoinColumn * /私人类别公共作用__construct(字符串标题这个->title =标题这个->createdAt =刚出现的\ DateTime ('现在');}/ /……getter / setter

模型的工厂

使用Foundry最好的方法是生成一个工厂每个实体类。你可以跳过这个使用匿名的工厂, 但模型工厂提供IDE自动完成功能并访问其他有用功能。

生成

使用maker命令为你的一个实体创建一个模型工厂:

1 2 3 4 5 6 7 8
创建工厂的实体类接下来:打开你的新工厂默认值/状态。

此命令将生成一个PostFactory类,看起来像这样:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
//src/Factory/PostFactory.php名称空间应用程序工厂使用应用程序实体帖子使用应用程序存储库PostRepository使用Zenstruck铸造寄存代理使用Zenstruck铸造模型工厂使用Zenstruck铸造代理/ * * *@extendsmodelfactory  * *@methodstatic Post|Proxy (array $attributes = []) *@methodstatic Post[]|Proxy[] createMany(int $number, array|callable $attributes = []) *@methodstatic Post|Proxy find(object|array mixed $criteria) *@method静态帖子|代理findorcreate(数组$属性)*@methodstatic Post|Proxy first(string $sortedField = 'id') *@methodstatic Post|Proxy last(string $sortedField = 'id') *@methodstatic Post|代理随机(array $attributes = []) *@methodstatic Post|Proxy randomOrCreate(array $attributes = [])) *@methodstatic Post[]|Proxy[] all() *@method静态Post[]|代理[]findBy(数组$attributes)*@methodstatic Post[]|Proxy[] randomSet(int $number, array $attributes = [])) *@methodstatic Post[]|Proxy[] randomRange(int $min, int $max, array $attributes = [])) *@method静态postrepository | repositoryproxy存储库()*@methodPost|Proxy create(array|callable $attributes = []) */最后PostFactory延伸模型工厂公共作用__construct()::__construct ();//如果需要(https://github.com/zenstruck/foundry#factoriations --as-services)受保护的作用getDefaults()数组返回// TODO添加默认值(https://github.com/zenstruck/foundry#model-factories)];}受保护的作用初始化()自己/ /查看https://github.com/zenstruck/foundry初始化返回这个// ->afterInstantiate(function(Post $ Post) {}); }受保护的静态作用getClass()字符串返回帖子::类;} }

提示

使用:工厂——测试将在年内生成工厂测试/工厂

提示

phpstorm 2021.2+有支持泛型注释,有了它,你的工厂的注释可以减少到以下,并且仍然具有相同的自动完成支持:

1 2 3 4 5 6 7 8 9 10 11
/ * * *@extendsmodelfactory  * *@methodstatic Post[]|Proxy[] createMany(int $number, array|callable $attributes = []) *@method静态postrepository | repositoryproxy存储库()*@methodPost|Proxy create(array|callable $attributes = []) */最后PostFactory延伸模型工厂/ /……

getDefaults (),您可以返回一个数组,其中包含任何新对象应该具有的所有默认值。骗子可轻松获取随机数据:

1 2 3 4 5 6 7 8 9
受保护的作用getDefaults()数组返回//Sob娱乐下载ymfony的属性访问组件用于填充属性//这意味着setTitle()将被调用,或者你可以有一个$title构造函数参数“标题”=>自己::骗子()->独特的()->句子(),“身体”=>自己::骗子()->句子()];}

提示

最好是有getDefaults ()返回持久化有效对象的属性(所有非空字段)。

使用你的工厂

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
使用应用程序工厂PostFactory//使用来自`getDefaults()`的随机数据创建/持续帖子PostFactory::createone();//或者为某些属性提供值(其他属性将是随机的)PostFactory::创建([“标题”=>“我的名字”]);//createOne()返回封装在代理对象中的持久化Post对象帖子=工厂后::createone();//“代理”神奇地调用底层的Post方法,并被提示为“Post”标题帖子->getTitle ();//getTitle()可以由IDE自动完成!//如果你需要Post对象,使用->对象()realPost帖子->对象();//创建并保持5个随机数据来自getDefaults()PostFactory::createMany (5);/ /返回邮政代理[][]|PostFactory::createMany (5, [“标题”=>“我的名字”]);//为给定的属性查找一个持久化对象,如果没有找到,则使用该属性创建PostFactory::findorcreate([“标题”=>“我的名字”]);/ /返回帖子|代理PostFactory::第();//获取第一个对象(假定自动递增的“ID”列)PostFactory::第一的(“createdAt”);//假设“createdAt”是一个datetime列,它将返回最新的对象PostFactory::最后();//获取最后一个对象(假设一个自动递增的“id”列)PostFactory::最后一个(“createdAt”);//假设"createdAt"是一个datetime列,这将返回最老的对象PostFactory::截断();//清空数据库表PostFactory::count ();//持久化Posts的数量PostFactory::全部();// Post[]|Proxy[] all the持久化的PostPostFactory::findBy ([“作者”=>“凯文”]);// post [] | proxy []匹配过滤器帖子=工厂后::找到(5);// Post id为5的|代理帖子=工厂后::找到([“标题”=>“我的第一个帖子”]);// Post|匹配过滤器的代理//获取一个被持久化的随机对象帖子=工厂后::随机();/ /返回帖子|代理帖子=工厂后::随机([“作者”=>“凯文”]);//按传递的属性进行筛选//或自动持久化一个新的随机对象,如果不存在帖子=工厂后::randomOrCreate ();帖子=工厂后::randomOrCreate ([“作者”=>“凯文”]);//按传递的属性筛选或创建//获取已持久化的随机对象集的帖子=工厂后::随机集(4);//包含4个Post|Proxy对象的数组的帖子=工厂后::随机集(4, [“作者”=>“凯文”]);//按传递的属性进行筛选//持久化对象的随机范围的帖子=工厂后::randomRange (05);//包含0-5个Post|Proxy对象的数组的帖子=工厂后::randomRange (05, [“作者”=>“凯文”]);//按传递的属性进行筛选

可重用模型工厂“状态”

你可以添加任何你想要的方法到你的模型工厂(即静态方法,以某种方式创建一个对象),但你也可以添加

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
名称空间应用程序工厂使用应用程序实体帖子使用Zenstruck铸造模型工厂最后PostFactory延伸模型工厂/ /……公共作用发表()自己//调用setPublishedAt()并传递一个随机的DateTime返回这个->addState([“published_at”=>自己::骗子()->dateTime ()));}公共作用未发表的()自己返回这个->addState([“published_at”=>]);}公共作用withviewcount.(int.= null)自己返回这个->addState (作用()使用返回“查看次数”=>??自己::骗子()->numberBetween (010000));});} }

您可以使用状态使您的测试非常明确,以提高可读性:

12 3 4 5 6 7 8 9 10 11 12 13 14 15
//不使用构造函数"new PostFactory()"),但使用/ /新()方法。定义状态后,调用“create()”来创建//持久化模型。帖子=工厂后::刚出现的()->未发表的()->create ();帖子=工厂后::刚出现的()->withViewCount(3.->create ();//合并多个状态帖子=工厂后::刚出现的()->未发表的()->withViewCount(10->create ();//不需要参数的状态可以作为字符串添加到PostFactory::new()帖子=工厂后::刚出现的“发布”“withViewCount”->create ();

属性

可以通过多种方式添加用于实例化对象的属性。属性可以是数组,或者一个可调用的它返回一个数组。使用一个可调用的确保在实例化期间为每个对象分别运行随机数据作为可调用对象。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
使用应用程序实体类别使用应用程序实体帖子使用应用程序工厂分类工厂使用应用程序工厂PostFactory使用作用Zenstruck铸造骗子// new()的第一个参数允许你重写默认值//在“hostDory :: getDefaults()`中定义的值的帖子=工厂后::刚出现的([“标题”=>“发布”])->withAttributes ([“身体”=>'帖子身体......'//将使用CategoryFactory为每个Post创建一个新的Category“类别”= > CategoryFactory::刚出现的([“名字”=>“php”))))->withAttributes ([//代理被自动转换为它们包装的对象“类别”= > CategoryFactory::createOne ())->withAttributes (作用()返回“createdAt”= >骗子()->dateTime ()];})//查看下面的faker部分//创建“2”帖子->许多(2->创建([“标题”=>“不同的标题”]);的帖子0->getTitle ();/ /“不同的标题”的帖子0->getBody ();/ /“后的身体……”的帖子0->getCategory();//随机类别的帖子0->getPublishedAt ();/ / \ DateTime(上周)的帖子0->getCreatedAt ();//随机\ datetime的帖子1->getTitle ();/ /“不同的标题”的帖子1->getBody ();/ /“后的身体……”的帖子1->getCategory();//随机类别(与上述不同)的帖子1->getPublishedAt ();/ / \ DateTime(上周)的帖子1->getCreatedAt ();//随机\DateTime(与上面不同)

骗子

该库提供了一个包装器菲拉伯为了帮助您的工厂生成随机数据:

1 2 3 4 5 6 7
使用Zenstruck铸造工厂使用作用Zenstruck铸造骗子;工厂::骗子()->名称();/ /随机名称//或者使用helper函数骗子()->电子邮件();/ /随机邮件

请注意

你可以自己注册摊贩\发电机

1 2 3 4 5 6
#配置/包/ dev / zenstruck_foundry.yaml(see Bundle Configuration section about sharing this in the test environment)zenstruck_foundry:骗子:区域设置:fr_FR#设置区域设置# 或者服务:my_faker#使用您自己的Faker\Generator实例进行完全控制

事件/挂钩

以下事件可以添加到工厂。可以添加多个事件回调,它们按照添加的顺序运行。

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
使用应用程序工厂PostFactory使用Zenstruck铸造代理;PostFactory::刚出现的()->beforeInstantiate (作用(数组属性数组// $属性是将用于实例化对象的原因,根据需要操作属性“标题”] =“不同的标题”返回属性//必须返回最后的$属性})->下班(作用(文章对象, 大批属性无效的// $object是实例化的对象// $属性包含用于实例化对象和任何附加的属性})->afterPersist (作用(代理对象, 大批属性/*@var.Post对象* /美元//仅当对象被持久化时才会调用此事件// $proxy是一个包装持久化对象的代理// $属性包含用于实例化对象和任何附加的属性})//如果第一个参数是类型暗示的对象,它将被传递给闭包(而不是代理)->afterPersist (作用(文章对象, 大批属性//仅当对象被持久化时才会调用此事件// $object是持久化的Post对象// $属性包含用于实例化对象和任何附加的属性})//允许多个事件->beforeInstantiate (作用属性返回属性; })->下班(作用(){})->afterPersist (作用(){});

也可以直接在模型工厂类中添加挂钩:

1 2 3 4 5 6
受保护的作用初始化()自己返回这个->afterPersist (作用(){});}

初始化了解更多关于初始化()方法。

初始化

您可以覆盖您的模型工厂初始化()方法添加默认状态/逻辑:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
名称空间应用程序工厂使用应用程序实体帖子使用Zenstruck铸造模型工厂最后PostFactory延伸模型工厂/ /……受保护的作用初始化()自己返回这个->(出版)//默认发布->instantiateWith (作用(数组属性返回刚出现的Post ();//该工厂的自定义实例化})->afterPersist (作用(){})//该工厂的默认事件; }}

请注意

一定要将各州/挂钩连锁$这一点因为工厂是不可变

实例化

默认情况下,对象是通过使用对象的构造函数以正常方式实例化的。使用与构造函数参数匹配的属性。其余的属性使用Symfony设置为对象ob娱乐下载PropertyAccess组件(setter /公共属性)。任何额外的属性都会引发异常。

你可以用几种方式定制实例化器:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
使用应用程序实体帖子使用应用程序工厂PostFactory使用Zenstruck铸造Instantiator//设置当前工厂的实例化器PostFactory::刚出现的()//实例化对象而不调用构造函数->instantiateWith ((刚出现的Instantiator ())->withoutConstructor ())//“foo”和“bar”属性在实例化时被忽略->instantiateWith ((刚出现的Instantiator ())->allowExtraAttributes ([“foo”“酒吧”)))//实例化时忽略所有额外的属性->instantiateWith ((刚出现的Instantiator ())->allowExtraAttributes())//在实例化时强制设置title和body->instantiateWith ((刚出现的Instantiator ())->alwaysForceProperties([“标题”“身体”)))//不使用setter,总是“强制设置”属性(即使是private/protected,也不使用setter)->instantiateWith ((刚出现的Instantiator ())->alwaryforceproperties())//可以组合不同的“模式”->instantiateWith ((刚出现的Instantiator ())->flyconstructor()->allowExtraAttributes ()->alwaryforceproperties())//实例化器只是一个可调用对象,你可以提供自己的->instantiateWith (作用(数组att、字符串对象返回刚出现的Post ();/ /……你自己的逻辑}) ;

你可以为你的所有工厂定制全局实例化器(仍然可以被工厂实例化器推翻):

1 2 3 4 5 6 7 8
#配置/包/ dev / zenstruck_foundry.yaml(see Bundle Configuration section about sharing this in the test environment)zenstruck_foundry:instantiator:without_constructor:真正的#总是实例化对象而不调用构造函数allow_extra_attributes:真正的#总是忽略额外的属性always_force_properties:真正的#始终为“力集”属性# 或者服务:my_instantiator.#您自己的可调用服务,以实现完全控制

不可变

工厂的属性是不可变的:

1 2 3 4 5 6 7 8
使用应用程序工厂PostFactory工厂=工厂后::刚出现的();factory1工厂->冒险([]);//返回一个新的PostFactory对象factory2工厂->instantiateWith (作用(){});//返回一个新的PostFactory对象factory3工厂->beforeInstantiate (作用(){});//返回一个新的PostFactory对象factory4工厂->下班(作用(){});//返回一个新的PostFactory对象factory5工厂->afterPersist (作用(){});//返回一个新的PostFactory对象

原则的关系

假设你的实体遵循原则关系的最佳实践你用的是默认instantiator、铸造只是工作与原则的关系。不同的关系和创建实体的方式有一些细微差别。下面尝试为每种关系类型记录这些内容。

多对一

以下假设评论实体与具有多对一关系帖子

1 2 3 4 5 6 7 8 9 10 11 12 13 13 14 15 16 18 19 20 21 21 22 23 23 22 29 29 29 29 29 29 30 32 31 32
使用应用程序工厂CommentFactory使用应用程序工厂PostFactory//示例1:预创建Post并附加到Comment帖子=工厂后::createone();//代理的实例CommentFactory::创建([“职位”=>帖子]);工厂::创建([“职位”=>帖子->对象()));//函数与上面相同//示例2:预创建Posts并随机选择一个PostFactory::createMany (5);//创建5个帖子CommentFactory::创建([“职位”= > PostFactory::随机()));//或者创建多个,每个都有一个不同的随机PostCommentFactory::createMany (5//创建5个注释作用()//注意回调-这确保每个5个注释都有一个不同的Post返回“职位”= > PostFactory::随机();//每个注释集从数据库中已经存在的那些随机Post});//示例3:为每个注释创建一个单独的PostCommentFactory::createMany (5, [//该属性是PostFactory的一个实例,为每个创建的Comment单独创建“职位”= > PostFactory::刚出现的(),]);//示例4:使用同一个Post创建多个CommentsCommentFactory::createMany (5, [“职位”= > PostFactory::createone(),//注意这里的“createOne()]);

提示

建议您定义的唯一关系ModelFactory: getDefaults ()非空多对一。

提示

我们也建议你ModelFactory: getDefaults ()返回一个工厂而不是被创建的实体:

12 3 4 5 6 7 8 9 10 11 12
受保护的作用getDefaults()数组返回/ /推荐“职位”= > PostFactory::刚出现的(),“职位”= > PostFactory::刚出现的()->发表(),//不推荐-可能会导致额外的非预期职位“职位”= > PostFactory::createone(),“职位”= > PostFactory::刚出现的()->(出版)->create ());}

一对多

以下假设帖子实体与具有一对多关系评论

1 2 3 4 5 6 7 8 9 10 11
使用应用程序工厂CommentFactory使用应用程序工厂PostFactory//示例1:创建一个有6个评论的帖子PostFactory::创建([“评论”= > CommentFactory::刚出现的()->许多(6)));//示例2:创建6个帖子,每个帖子有4条评论(总共24条评论)PostFactory::createMany (6, [“评论”= > CommentFactory::刚出现的()->许多(4)));//示例3:创建6个帖子,每个帖子在0到10条评论之间PostFactory::createMany (6, [“评论”= > CommentFactory::刚出现的()->许多(010)));

多对多

以下假设帖子实体与具有多对多关系标签

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
使用应用程序工厂PostFactory使用应用程序工厂TagFactory//示例1:预创建标签并附加到Post标签= TagFactory::createMany (3.);PostFactory::创建([“标签”=>标签]);//示例2:预创建标签并选择一个随机集TagFactory::createMany (10);PostFactory::刚出现的()->许多(5//创建5个帖子->创建(作用()//注意回调-这确保每个5个帖子有一个不同的随机集返回“标签”= > TagFactory::随机集(2));//每个帖子使用2个随机标签从那些已经在数据库中}) ;//示例3:预创建标记并选择一个随机范围TagFactory::createMany (10);PostFactory::刚出现的()->许多(5//创建5个帖子->创建(作用()//注意回调-这确保了5篇文章中的每一篇都有不同的随机范围返回“标签”= > TagFactory::randomRange (05));//每个帖子使用0到5个随机标签从那些已经在数据库中}) ;//示例4:创建3篇文章,每个文章都有3个唯一的标签PostFactory::createMany (3., [“标签”= > TagFactory::刚出现的()->许多(3.)));//示例5:创建3个post,每个post都有0到3个唯一的标签PostFactory::createMany (3., [“标签”= > TagFactory::刚出现的()->许多(03.)));

工厂为服务

如果您的工厂需要依赖项,您可以将它们定义为服务。下面的示例演示了一个非常常见的用例:使用UserPasswordEncoderInterface服务。

12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/ / src /工厂/ UserFactory.php名称空间应用程序工厂使用应用程序实体用户使用ob娱乐下载组件安全核心编码器UserPasswordEncoderInterface使用Zenstruck铸造模型工厂最后UserFactory延伸模型工厂私人passwordEncoder公共作用__construct(UserPasswordEncoderInterfacepasswordEncoder::__construct ();这个->passwordEncoder =passwordEncoder; }受保护的作用getDefaults()数组返回'电子邮件'=>自己::骗子()->独特的()->safeEmail (),“密码”=>“1234”,];}受保护的作用初始化()自己返回这个->下班(作用(用户用户用户->设置密码(这个->passwordEncoder->encodePassword (用户用户->getPassword ()));});}受保护的静态作用getClass()字符串返回用户::类;} }

如果使用标准的Symfony Flex应ob娱乐下载用程序,这将自动连接/自动配置。如果不是,则注册服务和标记铸造厂

正常使用工厂:

1 2
UserFactory::创建([“密码”=>“mypass”])->getPassword ();/ /“mypass”编码UserFactory::createOne ()->getPassword ();//“1234”编码(因为“1234”被设置为默认密码)

请注意

提供的包是作为服务的工厂所需要的。

请注意

如果使用:工厂——测试,工厂将在测试/工厂目录不是在标准的Symfony Flex应用程序中自动连接/自动配置的。你必须手动注册这些服务。ob娱乐下载

匿名的工厂

铸造厂可用于为您没有模型工厂的实体创建工厂:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
使用应用程序实体帖子使用Zenstruck铸造AnonymousFactory使用作用Zenstruck铸造工厂使用作用Zenstruck铸造创建使用作用Zenstruck铸造创造许多工厂= AnonymousFactory::刚出现的(文章::类);工厂=工厂(职位::类);//上面的替代//具有与ModelFactory相同的API工厂->创建([“字段”=>“价值”]);工厂->许多(5->创建([“字段”=>“价值”]);工厂->instantiateWith (作用(){});工厂->beforeInstantiate (作用(){});工厂->下班(作用(){});工厂->afterPersist (作用(){});//为给定的属性查找一个持久化对象,如果没有找到,则使用该属性创建工厂->findorcreate([“标题”=>“我的名字”]);工厂->第();//获取第一个对象(假定自动递增的“ID”列)工厂->第一的(“createdAt”);//假设“createdAt”是一个datetime列,它将返回最新的对象工厂->最后();//获取最后一个对象(假设一个自动递增的“id”列)工厂->最后一个(“createdAt”);//假设"createdAt"是一个datetime列,这将返回最老的对象工厂->截断();//清空数据库表工厂->count ();//持久的帖子数量工厂->全部();//Post[]| Proxy[]所有持久化的Post工厂->findBy ([“作者”=>“凯文”]);// post [] | proxy []匹配过滤器工厂->找到(5);// Post id为5的|代理工厂->找到([“标题”=>“我的第一个帖子”]);// Post|匹配过滤器的代理//获取一个被持久化的随机对象工厂->随机();/ /返回帖子|代理工厂->随机([“作者”=>“凯文”]);//按传递的属性进行筛选//或自动持久化一个新的随机对象,如果不存在工厂->randomOrCreate ();工厂->randomOrCreate ([“作者”=>“凯文”]);//按传递的属性筛选或创建//获取已持久化的随机对象集工厂->随机集(4);//包含4个Post|Proxy对象的数组工厂->随机集(4, [“作者”=>“凯文”]);//按传递的属性进行筛选//持久化对象的随机范围工厂->randomRange (05);//包含0-5个Post|Proxy对象的数组工厂->randomRange (05, [“作者”=>“凯文”]);//按传递的属性进行筛选//存储库代理包装PostRepository(参见下面的存储库代理部分)工厂->存储库();//便利函数实体=创建(职位::类,“字段”=>“价值”]);实体= create_many(职位::类,5, [“字段”=>“价值”]);

不坚持

工厂也可以在不持久化对象的情况下创建对象。这对于单元测试非常有用,在单元测试中,您只想测试实际对象的行为,或者创建不是实体的对象。创建时,它们仍然被包装在代理以后可选地保存。

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
使用应用程序工厂PostFactory使用应用程序实体帖子使用Zenstruck铸造AnonymousFactory使用作用Zenstruck铸造实例化使用作用Zenstruck铸造例举许多帖子=工厂后::刚出现的()->withoutPersisting ()->create ();/ /返回帖子|代理帖子->setTitle (“其他的东西”);//用对象做点什么帖子->save ();//持续到post(save()是代理的方法)帖子=工厂后::刚出现的()->withoutPersisting ()->create ()->对象();//实际帖子对象的帖子=工厂后::刚出现的()->withoutPersisting ()->许多(5->create ();/ /返回邮政代理[][]|/ /匿名工厂:工厂刚出现的AnonymousFactory (Post::类);实体工厂->withoutPersisting ()->创建([“字段”=>“价值”]);/ /返回帖子|代理实体工厂->withoutPersisting ()->创建([“字段”=>“价值”])->对象();//实际帖子对象实体工厂->withoutPersisting ()->许多(5->创建([“字段”=>“价值”]);/ /返回邮政代理[][]|//便利函数实体=实例化(Post)::类,“字段”=>“价值”]);实体= instantiate_many(职位::类,5, [“字段”=>“价值”]);

如果您希望您的模型工厂在默认情况下不持久化,请重写它初始化()方法添加此行为:

1 2 3 4 5 6
受保护的作用初始化()自己返回这个->withoutPersisting ();}

现在,在使用这个工厂创建对象之后,您必须调用- - - - - - >保存()将它们实际持久化到数据库中。

提示

如果你想禁用所有模型工厂的默认持久化:

  1. 创建一个扩展的抽象模型工厂Zenstrick\Foundry\ModelFactory
  2. 覆盖初始化()方法如上所示。
  3. 让你们所有的模型工厂都从这里延伸。

使用doctrinefixturesbundle.

铸造厂用完了DoctrineFixturesBundle.你可以简单地在你的fixture文件中使用你的工厂和故事:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/ / src / DataFixtures / AppFixtures.php名称空间应用程序datafixtures.使用应用程序工厂分类工厂使用应用程序工厂CommentFactory使用应用程序工厂PostFactory使用应用程序工厂TagFactory使用学说FixturesBundle固定装置使用学说持久性ObjectManager.AppFixtures延伸固定装置公共作用负载(ObjectManager经理//创建10个类别分类工厂::createMany (10);//创建20个标签TagFactory::createMany (20.);//创建50个PostPostFactory::createMany (50.作用()返回//每个帖子都会有一个随机的分类(从上面创建的分类中选择)“类别”= > CategoryFactory::随机(),//每个Post将有0到6个标签(从上面创建的标签中选择)“标签”= > TagFactory::randomRange (06),//每个帖子都会在0到10条评论之间创建新的“评论”= > CommentFactory::刚出现的()->许多(010),);});} }

跑过原则:设备:负载正常情况下种子您的数据库。

在测试中使用

传统上,数据fixture定义在测试之外的一个或多个文件中。当使用这些fixture编写测试时,您的fixture是一种黑盒.固定装置之间没有明确的连接以及您正在测试的内容。

Foundry允许每个单独的测试完全遵循AAA("Arrange", "Act", "Assert")测试模式。您可以在每次测试开始时使用“工厂”创建fixture。您只创建适用于测试的fixture。此外,这些fixture仅使用测试所需的属性创建——不适用的属性将用随机数据填充。创建的fixture对象被包装在一个“代理”中,该代理帮助处理前置和后置断言。

让我们看一个例子:

1 2 3 4 5 6 7 8 9 10 11 12 13 13 14 15 16 18 19 20 21 21 22 23 23 22 29 29 29 29 29 29 30 32 31 32
公共作用test_can_post_a_comment.()无效的// 1.“安排”帖子=工厂后::刚出现的()//新建邮政工厂->(出版)//将文章设置为“已发表”状态->创建([//实例化Post对象并持久化“鼻涕虫”=>“发布”//此测试仅需要SLUI字段 - 所有其他字段都是随机数据]);/ / 1。“Pre-Assertions”这个->assertcount(0帖子->getComments ());/ / 2。“行动”静态::ensureKernelShutdown ();//创建工厂靴子内核;在创建客户端之前关闭客户端静态::createClient ();客户端->请求(“得到”“/文章/发布”);//注意排列步骤的块客户端->submitForm (“添加”, [“评论[名字]”=>“约翰。”的评论(身体)=>“我的评论”]);/ / 3。“维护”自己::assertResponseRedirects (“/文章/发布”);这个->assertcount(1帖子->refresh ()->getComments ());//从数据库刷新$post并调用->getComments()CommentFactory::断言()->存在([// Doctrine存储库断言“名字”=>“约翰。”“身体”=>“我的评论”]);}

在你的TestCase中启用Foundry

添加工厂使用工厂的测试性:

12 3 4 5 6 7 8 9 10 11 12 13 14 15
使用应用程序工厂PostFactory使用Zenstruck铸造测试工厂使用ob娱乐下载FrameworkBundle测试WebTestCaseMyTest延伸WebTestCase使用工厂公共作用test_1()无效的帖子=工厂后::createone();/ /……} }

数据库重启

这个库要求在每次测试之前重置数据库。打包的ResetDatabaseTrait帮你处理这个问题。

1 2 3 4 5 6 7 8 9 10
使用Zenstruck铸造测试工厂使用Zenstruck铸造测试ResetDatabase使用ob娱乐下载FrameworkBundle测试WebTestCaseMyTest延伸WebTestCase使用ResetDatabase工厂/ /……

在第一次测试之前使用ResetDatabaseTrait,它删除(如果存在)并创建测试数据库。然后,在默认情况下,在每次测试之前,使用原则:模式:下降/原则:模式:创建

或者,您可以通过设置env变量来让它运行迁移FOUNDRY_RESET_MODE =迁移(在你的.env.test).当使用这个模式,在每次测试之前,会删除/创建数据库并运行迁移(通过教义:迁移:迁移).这种模式确实会使您的测试套件变慢(特别是在需要大量迁移的情况下)。强烈推荐使用damadoctrinetestbundle.提高速度。启用此包时,数据库会被删除/创建并仅为套件迁移一次。

提示

使用工厂为测试创建一个基本的TestCase,以避免向每个TestCase添加特征。

提示

如果您的测试不坚持它们创建的对象不需要这些测试特征。

默认情况下,ResetDatabase重置默认配置的连接的数据库和默认配置的对象管理器的架构。要自定义要重置的连接和对象管理器(或重置多个连接/管理器),请设置以下环境变量:

1 2 3 4
# .env.testFOUNDRY_RESET_CONNECTIONS = connection1 connection2 FOUNDRY_RESET_OBJECT_MANAGERS = manager1 manager2

对象代理

由工厂创建的对象包装在特殊的代理目的。这些对象允许您的学说实体拥有活动记录喜欢行为:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
使用应用程序工厂PostFactory帖子=工厂后::createOne ()->创建([“标题”=>“我的名字”]);//使用Zenstruck\Foundry\Proxy//获取包装的对象realPost帖子->对象();// Post的实例//调用任何Post方法帖子->getTitle ();/ /“我的标题”//设置属性并保存到数据库帖子->setTitle (“新标题”);帖子->save ();//从数据库刷新帖子->refresh ();//从数据库删除帖子->remove ();帖子->存储库();//存储库代理包装PostRepository(参见下面的存储库代理部分)

力设置

对象代理有帮助器方法来访问它们所包装的对象的非公共属性:

1 2 3 4 5 5
//设置私有属性帖子->forceSet (“createdAt”刚出现的\约会时间());//获取私有/受保护的属性帖子->forceGet (“createdAt”);

自动刷新

对象代理具有启用选项自动刷新这就消除了调用的需要- > refresh ()在调用底层对象的方法之前。当启用自动刷新时,大多数对代理对象的调用都会首先刷新数据库中包装的对象。

1 2 3 4 5 6 7 8 9 10
使用应用程序工厂PostFactory帖子=工厂后::刚出现的([“标题”=>'原始标题'])->create ()->enableAutoRefresh ();//…将$post标题更改为“新标题”的逻辑(如功能测试)帖子->getTitle ();//“新标题”(相当于$ post-> refresh() - > gettitle())

如果没有启用自动刷新,则上述调用post - > getTitle ()将返回“原始标题”。

请注意

使用自动刷新时需要注意的一种情况是,所有方法都首先刷新对象。如果通过多个方法(或多个强制集)改变对象的状态,则会抛出一个“unsaved changes”异常:

1 2 3 4 5 6 7 8 9
使用应用程序工厂PostFactory帖子=工厂后::刚出现的([“标题”=>'原始标题'“身体”=>“原始机构”])->create ()->enableAutoRefresh ();帖子->setTitle (“新标题”);帖子->挫折体(“新的身体”);//由于上面的“未保存的变更”为$ POST抛出异常

要克服此问题,您需要首先禁用自动刷新,然后在制作/保存更改后重新启用:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
使用应用程序实体帖子使用应用程序工厂PostFactory帖子=工厂后::刚出现的([“标题”=>'原始标题'“身体”=>“原始机构”])->create ()->enableAutoRefresh ();帖子->disableAutoRefresh ();帖子->setTitle (“新标题”);//或者使用->forceSet('title', 'New title')帖子->挫折体(“新的身体”);//或使用 - >部队('Body','New Body')帖子->enableAutoRefresh ();帖子->save ();帖子->getBody ();/ /“新身体”帖子->getTitle ();/ /“新标题”//或者,使用->withoutAutoRefresh()助手,它首先禁用自动刷新,然后在之后重新启用//执行回调函数帖子->没有utorefresh(作用(文章帖子//可以传递Post或Proxy给回调函数帖子->setTitle (“新标题”);帖子->挫折体(“新的身体”);});帖子->save ();//如果强制设置属性,您可以使用 - > forctoreTall()帮助:帖子->forceSetAll ([“标题”=>“新标题”“身体”=>“新的身体”]);帖子->save ();

请注意

您可以启用/禁用全局自动刷新功能,使每个代理在默认情况下都可自动刷新。当启用时,您将不得不这样做选择退出自动刷新。

  • YAML
1 2 3
#配置/包/ dev / zenstruck_foundry.yaml(see Bundle Configuration section about sharing this in the test environment)zenstruck_foundry:auto_refresh_proxies:真正的/错误

存储库代理

本图书馆提供存储库代理封装对象存储库以提供有用的断言和方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
使用应用程序实体帖子使用应用程序工厂PostFactory使用作用Zenstruck铸造存储库//存储代理的实例存储库=工厂后::存储库();//为没有创建模型工厂的存储库提供代理存储库=库(职位::类);//有帮助的方法-所有返回的对象都被代理存储库->count ();//数据库表中的行数数数(存储库);//相当于上面(存储族普罗文实现\可数)存储库->第();//获取第一个对象(假定自动递增的“ID”列)存储库->第一的(“createdAt”);//假设“createdAt”是一个datetime列,它将返回最新的对象存储库->最后();//获取最后一个对象(假设一个自动递增的“id”列)存储库->最后一个(“createdAt”);//假设"createdAt"是一个datetime列,这将返回最老的对象存储库->截断();//删除数据库表中的所有行存储库->随机();//获取一个随机对象存储库->随机([“作者”=>“凯文”]);//通过传递的条件获取过滤的随机对象存储库->随机集(5);//获取5个随机对象存储库->随机集(5, [“作者”=>“凯文”]);//按通过的条件筛选5个随机对象存储库->randomRange (05);//获取0-5个随机对象存储库->randomRange (05, [“作者”=>“凯文”]);//获取0-5个随机对象//对象存储库的实例-所有返回的对象都是代理的存储库->找到(1);/ /代理| |空存储库->找到([“标题”=>“我的名字”]);/ /代理| |空存储库->findOneBy ([“标题”=>“我的名字”]);/ /代理| |空存储库->findAll();// proxy [] | post []迭代器到数组(存储库);//等价于上面(RepositoryProxy实现\IteratorAggregate)存储库->findBy ([“标题”=>“我的名字”]);// proxy [] | post []//可以调用底层存储库上的方法-返回的对象被代理存储库->findOneByTitle (“我的名字”);/ /代理| |空

断言

对象代理和模型工厂都有有用的PHPUnit断言:

12 3 4 5 6 7 8 9 10 11 12 13 14 15
使用应用程序工厂PostFactory帖子=工厂后::createone();帖子->assertPersisted ();帖子->assertNotPersisted ();PostFactory::断言()->();PostFactory::断言()->数数(3.);PostFactory::断言()->countGreaterThan (3.);PostFactory::断言()->countgreatthanorequal(3.);PostFactory::断言()->countLessThan (3.);PostFactory::断言()->countLessThanOrEqual (3.);PostFactory::断言()->存在([“标题”=>“我的名字”]); 邮局::断言()->notExists ([“标题”=>“我的名字”]);

全局状态

如果要为所有测试设置初始数据库状态,则可以在测试/ bootstrap.php

1 2 3 4 5 6 7
//tests/bootstrap.php/ /……zenstruct \ foundry \ test \ teststate::addGlobalState (作用(){compounctFactory.::创建([“名字”=>“php”]);分类工厂::创建([“名字”=>sob娱乐下载ymfony的]); });

为了避免引导文件变得过于复杂,最好将全局状态封装到故事

1 2 3 4 5 6
//tests/bootstrap.php/ /……zenstruct \ foundry \ test \ teststate::addGlobalState (作用(){全球历史::load ();});

请注意

你仍然可以访问故事的状态全球国家的故事在你的测试中,它们仍然只加载一次。

请注意

ResetDatabase使用全局状态时需要trait。

PHPUnit数据提供者

用工厂是可能的PHPUnit数据提供者

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
使用应用程序工厂PostFactory/ * * *@dataProviderpostDataProvider * /公共作用test_post_via_data_provider(后续工作工厂无效的帖子工厂->create ();/ /……公共静态作用postDataProvider()可迭代的产量[PostFactory::刚出现的());产量[PostFactory::刚出现的()->发表()];}

请注意

确保您的数据提供商只能返回实例模型工厂你也不会试着打电话- > create ()在他们身上。数据提供者是在启动Foundry之前的phpunit过程中计算的。

请注意

出于同样的原因,它是不可能使用的工厂为服务使用必需的构造函数参数(容器还不可用)。

性能

以下是提高测试套件速度的可能选项。

DAMADoctrineTestBundle

这个库与DAMADoctrineTestBundle将每个测试打包到一个事务中,这将极大地减少测试时间。启用这个包后,这个库的测试套件的运行速度提高了5倍。

按照其文档进行安装。欧宝官网下载app铸造的ResetDatabase当使用包时,Trait检测并相应地调整。在运行测试套件之前,您的数据库仍然会被重置,但是模式不会在每次测试之前被重置(只是第一次)。

请注意

如果使用全局状态,它将在运行测试套件之前保留到数据库(而不是事务中)。如果您具有复杂的全局状态,这将进一步提高测试速度。

杂项

  1. 运行测试时禁用调试模式。在你的.env.test文件,您可以设置APP_DEBUG = 0在没有调试模式的情况下运行测试。这可以大大加快测试速度。在运行测试套件之前,您需要确保已清除缓存。做这件事最好的地方是在你的房间里测试/ bootstrap.php

    1 2 3 4 5 6
    //tests/bootstrap.php/ /……如果= = =(保龄球)_SERVER“APP_DEBUG”){//确保缓存新鲜刚出现的ob娱乐下载Symfony \文件系统组件\ \文件系统())->除去(__DIR__“/ . . / var /缓存/测试”);}
  2. 减少密码编码器工作因素.如果您有很多使用编码密码的测试,这将导致这些测试不必要地慢。你可以通过降低速度来提高速度工作因素你的编码器:

    1 2 3 4 5 6 7 8 9
    #配置/包/测试/ security.yaml编码器:#在此处使用您的用户类名应用实体\ \用户:#这个值应该与config/packages/security.yaml中的值相同算法:汽车成本:4#Bcrypt的最低可能值time_cost:3.#氩的最低可能值内存成本:10#氩的最低可能值
  3. 使用已知值对用户密码进行预编码bin /控制台安全:encode-password把这个放进去ModelFactory: getDefaults ().将已知值加为a常量在你的工厂:

    12 3 4 5 6 7 8 9 10 11 12
    UserFactory延伸模型工厂公共常量默认密码=“1234”//创建预编码版本的密码受保护的作用getDefaults()数组返回/ /……“密码”=>“argon2id v = 19美元= 65536美元,t = 4, p = 1 pLFF3D2gnvDmxMuuqH4BrA 3美元vkfv0cw + 6 eanspq9btvayc + jCOqrmWRstInB2fRPeQ ', ]; } }

    现在,在您的测试中,当您需要访问使用UserFactory使用UserFactory: DEFAULT_PASSWORD

没有测试

Foundry可以用于标准的PHPUnit单元测试(TestCase的扩展phpunit \ framework \ testcase而不是ob娱乐下载\包\ FrameworkBundle\测试\ KernelTestCase).这些测试仍然需要使用工厂trait将会启动铸造,但不会有教条可用。在这些测试中创建的工厂将不会被持久化(调用- > withoutPersisting ()没有必要)。因为bundle在这些测试中不可用,所以您拥有的任何bundle配置都不会被选中。您将需要添加仅测试配置.不幸的是,这可能意味着在这里复制您的bundle配置。

12 3 4 5 6 7 8 9 10 11 12 13 14 15
使用应用程序工厂PostFactory使用菲普尼特框架TestCase使用Zenstruck铸造测试工厂MyUnitTest延伸TestCase使用工厂公共作用some_test()无效的帖子=工厂后::createone();//$post未持久化到数据库} }

请注意

工厂为服务故事作为服务带必需构造函数的参数在非内核测试中不可用。容器无法解析它们的依赖关系。最简单的解决方法是将测试作为ob娱乐下载\包\ FrameworkBundle\测试\ KernelTestCase所以容器是可用的。

仅测试配置

你可以用纯PHP静态配置Foundry测试/ bootstrap.php.如果您有一个内核和核心,这很有用没有测试或者,如果不使用Bundle

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
//tests/bootstrap.php/ /……//配置默认实例化器zenstruct \ foundry \ test \ teststate::setInstantiator ((刚出现的Zenstruck \铸造\ Instantiator ())->flyconstructor()->allowExtraAttributes ()->alwaysForceProperties ());//配置一个自定义fakerzenstruct \ foundry \ test \ teststate::setFaker(骗子\工厂::创建(“fr_FR”));//启用全局自动刷新zenstruct \ foundry \ test \ teststate::enableDefaultProxyAutoRefresh ();//禁用全局自动刷新zenstruct \ foundry \ test \ teststate::disabledefaultproxyautorefresh();

请注意

如果使用包配置还有只测试配置将覆盖捆绑配置。

不使用Bundle

提供的包并不是严格要求使用Foundry进行测试。您可以在您的测试/目录。您可以使用旧铸造厂配置仅测试配置

故事

如果您找到您的测试,故事是有用的安排第二步是变得复杂(加载大量设备)或在测试和/或开发设备之间复制逻辑。它们用于提取特定的数据库状态成一个故事.故事可以加载到fixture和测试中,它们也可以依赖于其他故事。

使用maker命令创建故事:

1
垃圾箱/控制台:故事帖子

请注意

创建PostStory.phpsrc /故事,添加——测试要创建的标志测试/故事

修改构建设置此故事状态的方法:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
//src/Story/PostStory.php名称空间应用程序故事使用应用程序工厂分类工厂使用应用程序工厂PostFactory使用应用程序工厂TagFactory使用Zenstruck铸造故事最后PostStory延伸故事公共作用构建()无效的//创建10个类别分类工厂::createMany (10);//创建20个标签TagFactory::createMany (20.);//创建50个PostPostFactory::createMany (50.作用()返回//每个帖子都有一个随机类别(上面创建)“类别”= > CategoryFactory::随机(),//每个Post将在0到6个Tag之间(上面创建的)“标签”= > TagFactory::randomRange (06),);});} }

在您的测试、开发fixture或其他故事中使用新的故事:

1 2 3
PostStory::load ();//加载PostStory中定义的状态::build()PostStory::load ();//什么也不做-已经加载

请注意

故事中持久化的对象在每次测试后被清除(除非它是全球国家的故事).

故事作为服务

如果您的故事需要依赖项,则可以将它们定义为服务:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
//src/Story/PostStory.php名称空间应用程序故事使用应用程序工厂PostFactory使用应用程序服务ServiceA使用应用程序服务ServiceB使用Zenstruck铸造故事最后PostStory延伸故事私人服务A私人服务B公共作用__construct(ServiceA服务A,服务服务B这个->serviceA =服务A这个->serviceB =服务B; }公共作用构建()无效的//可以使用$this->serviceA, $this->serviceB来帮助构建这个故事} }

如果使用标准的Symfony Flex应ob娱乐下载用程序,这将自动连接/自动配置。如果不是,则注册服务和标记foundry.story

请注意

提供的包是作为服务的故事所必需的。

故事的状态

的另一个特点故事是他们的能力还记得他们创建的对象以后引用:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/ / src /故事/ CategoryStory.php名称空间应用程序故事使用应用程序工厂分类工厂使用Zenstruck铸造故事最后分类延伸故事公共作用构建()无效的这个->add (“php”, CategoryFactory::创建([“名字”=>“php”)));//在添加状态时创建工厂这个->add (sob娱乐下载ymfony的, CategoryFactory::刚出现的([“名字”=>sob娱乐下载ymfony的)));} }

稍后,您可以在创建其他装置时访问故事的状态:

1 2 3 4
PostFactory::创建([“类别”=>分类目录::加载()->get (“php”)));//或使用魔术方法(功能等同于上述)PostFactory::创建([“类别”=>分类目录::php ()));

请注意

故事状态在每次测试后被清除(除非它是全球国家的故事).

包配置

因为bundle是用来在你的dev测试环境,您希望每个环境的配置匹配。最简单的方法是让您的测试配置,进口dev.这样,只有一个地方来设置配置。

  • YAML
1 2 3 4 5 6 7 8 9 10
#配置/包/ dev / zenstruck_foundry.yamlzenstruck_foundry:#……#配置/包/测试/ zenstruck_foundry.yaml#只是导入dev配置进口:-资源:. . / dev / zenstruck_foundry.yaml

完全默认Bundle配置

  • YAML
  • PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
zenstruck_foundry:#是否默认自动刷新代理(https://github.com/zenstruck/foundry#auto-refresh)auto_refresh_proxies:真正的#配置faker被你的工厂使用。骗子:#修改faker的默认区域设置。区域设置:#例子:fr_FR#定制faker服务。服务:#示例:my_faker#配置你的工厂使用的默认实例化器。instantiator:#是否在实例化过程中调用对象的构造函数。without_constructor:#是否允许额外属性。allow_extra_attributes:#是否跳过setter并直接强制设置对象属性(public/private/protected)。always_force_properties:#自定义Instantiator服务。服务:#例子:my_instantiator