kesha-dev / yii2-save-relations-behavior
自动验证和保存相关 Active Record 模型。
Requires
- yiisoft/yii2: >=2.0.14
Requires (Dev)
- phpunit/phpunit: 4.8.36
This package is not auto-updated.
Last update: 2024-09-23 10:55:28 UTC
README
自动验证和保存相关 Active Record 模型。
特性
- 支持
hasMany()
和hasOne()
关系 - 适用于现有和新相关模型
- 支持复合主键
- 仅使用纯 Active Record API,因此应与任何 DB 驱动程序兼容
- 从 1.5.0 版本开始,现在可以与主模型一起删除相关记录
- ⚠️ 从 2.0.0 版本开始,关系属性现在遵守
safe
验证规则
安装
安装此扩展的首选方式是通过 composer。
运行以下命令
php composer.phar require --prefer-dist la-haute-societe/yii2-save-relations-behavior "*"
或添加
"la-haute-societe/yii2-save-relations-behavior": "*"
到您的 composer.json
文件的 require 部分。
配置
按照以下方式配置模型
use lhs\Yii2SaveRelationsBehavior\SaveRelationsBehavior; class Project extends \yii\db\ActiveRecord { use SaveRelationsTrait; // Optional public function behaviors() { return [ 'timestamp' => TimestampBehavior::className(), 'blameable' => BlameableBehavior::className(), ... 'saveRelations' => [ 'class' => SaveRelationsBehavior::className(), 'relations' => [ 'company', 'users', 'projectLinks' => ['cascadeDelete' => true], 'tags' => [ 'extraColumns' => function ($model) { /** @var $model Tag */ return [ 'order' => $model->order ]; } ] ], ], ]; } public function rules() { return [ [['name', 'company_id'], 'required'], [['name'], 'unique', 'targetAttribute' => ['company_id', 'name']], [['company', 'users', 'projectLinks', 'tags'], 'safe'] ]; } public function transactions() { return [ self::SCENARIO_DEFAULT => self::OP_ALL, ]; } ... /** * @return ActiveQuery */ public function getCompany() { return $this->hasOne(Company::className(), ['id' => 'company_id']); } /** * @return ActiveQuery */ public function getProjectUsers() { return $this->hasMany(ProjectUser::className(), ['project_id' => 'id']); } /** * @return ActiveQuery */ public function getUsers() { return $this->hasMany(User::className(), ['id' => 'user_id'])->via('ProjectUsers'); } /** * @return ActiveQuery */ public function getProjectLinks() { return $this->hasMany(ProjectLink::className(), ['project_id' => 'id']); } /** * @return ActiveQuery */ public function getTags() { return $this->hasMany(Tag::className(), ['id' => 'tag_id'])->viaTable('ProjectTags', ['project_id' => 'id']); } }
虽然不是强制性的,但强烈建议激活拥有模型的事务。⚠️ 为了保存,关系属性必须定义为拥有模型验证规则中的
safe
。
用法
现在可以按照以下方式设置和保存 relations
行为参数中声明的每个关系
$project = new Project(); $project->name = "New project"; $project->company = Company::findOne(2); $project->users = User::findAll([1,3]); $project->save();
您可以通过仅指定其主键来设置相关模型
$project = new Project(); $project->name = "Another project"; $project->company = 2; $project->users = [1,3]; $project->save();
您甚至可以像这样设置相关模型为关联数组
$project = Project::findOne(1); $project->company = ['name' => 'GiHub', 'description' => 'Awesome']; // Will create a new company record // $project->company = ['id' => 3, 'name' => 'GiHub', 'description' => 'Awesome']; // Will update an existing company record $project->save();
将使用 `load()` 方法大量分配相关模型的属性。所以请记住,在相关模型的规则中声明相应的属性为 safe。
注意: 只有新创建或更改的相关模型将被保存。请参阅 PHPUnit 测试以获取更多示例。
在多对多关系中填充额外的连接表列
在涉及连接表的多对多关系中,可以为每个模型保存到连接表中的附加列值。请参阅配置部分中的示例。
注意: 如果为关系配置了连接表属性,则将在每次保存时删除与相关模型关联的连接表中的行并重新插入,以确保连接表属性的变化也被保存。
验证
在保存之前将对每个声明的相关模型进行验证。如果任何验证失败,对于每个有错误的关联模型属性,将向拥有模型添加与命名关系相关联的错误。
对于 hasMany()
关系,将使用相关模型的索引来识别关联的错误消息。
可以通过声明包含必须包含所需场景值的 scenario
键的关联数组来为每个关系指定验证场景。例如,在以下配置中,将使用 Link::SOME_SCENARIO
场景验证 links
相关记录
... public function behaviors() { return [ 'saveRelations' => [ 'class' => SaveRelationsBehavior::className(), 'relations' => ['company', 'users', 'links' => ['scenario' => Link::SOME_SCENARIO]] ], ]; } ...
也可以在运行时使用 setRelationScenario
设置关系场景
$model->setRelationScenario('relationName', 'scenarioName');
提示:对于不涉及连接表的通过使用
via()
或viaTable()
方法的关系,您应该从 'required' 验证规则中删除指向拥有模型的属性,以便能够通过验证。
注意:如果在保存相关记录的afterSave事件过程中发生任何错误,第一次出现的错误将抛出
yii\db\Exception
。错误信息将附加到拥有模型的关联属性上。为了以用户友好的方式处理这些情况,必须捕获yii\db\Exception
异常。
当主模型被删除时删除相关记录
对于没有内置关联约束的数据库管理系统,从1.5.0版本开始,现在可以指定要删除的关联,包括主模型。
要这样做,关系应该声明为具有设置为true的cascadeDelete
属性。例如,当主模型被删除时,相关的projectLinks
记录将自动被删除。
... 'saveRelations' => [ 'class' => SaveRelationsBehavior::className(), 'relations' => [ 'projectLinks' => ['cascadeDelete' => true] ], ], ...
注意:将删除主模型在其
ActiveQuery
语句中定义的关联的所有记录。
用输入数据填充模型及其关联
此行为添加了一个方便的方法来以与load()方法相同的方式加载关系模型的属性。只需用相应的输入数据调用loadRelations()
即可。
例如
$project = Project::findOne(1); /** * $_POST could be something like: * [ * 'Company' => [ * 'name' => 'YiiSoft' * ], * 'ProjectLink' => [ * [ * 'language' => 'en', * 'name' => 'yii', * 'link' => 'https://yiiframework.cn' * ], * [ * 'language' => 'fr', * 'name' => 'yii', * 'link' => 'http://www.yiiframework.fr' * ] * ] * ]; */ $project->loadRelations(Yii::$app->request->post());
您还可以通过将SaveRelationsTrait
添加到模型中来进一步简化此过程。在这种情况下,对load()
方法的调用也将自动触发对loadRelations()
方法的调用,使用相同的数据,因此您基本上不需要更改控制器。
relationKeyName
属性可以用来决定如何从数据参数中检索关系数据。
可能的常量值包括
SaveRelationsBehavior::RELATION_KEY_FORM_NAME
(默认值):键名将使用模型的formName()
方法计算得出SaveRelationsBehavior::RELATION_KEY_RELATION_NAME
:使用在行为声明中定义的关系名称
获取旧的关系值
要检索模型保存前最近的修改之前的关系值,可以使用以下方法
getOldRelation($name)
:获取一个命名关系的旧值。getOldRelations()
:获取一个数组,其中包含以名称为索引的旧值关系。
备注
- 如果关系尚未被修改,则返回其初始值
- 仅返回在行为参数中定义的关系
获取脏关系
要处理自模型加载以来已修改的脏(修改)关系,可以使用以下方法
getDirtyRelations()
:获取自加载以来已被修改的关系(名称-值对)markRelationDirty($name)
:即使尚未修改,也将关系标记为脏。