la-haute-societe / yii2-save-relations-behavior
自动验证并保存相关 Active Record 模型。
Requires
- yiisoft/yii2: >=2.0.14
Requires (Dev)
- phpunit/phpunit: 4.8.36
README
自动验证和保存相关 Active Record 模型。
功能
- 支持
hasMany()
和hasOne()
关系 - 与现有以及新相关模型一起工作
- 支持复合主键
- 仅使用纯 Active Record API,因此它应与任何数据库驱动程序一起工作
- 从 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()
方法时,应从‘必需’验证规则中删除指向拥有者模型的属性,以便能够通过验证。
注意:如果在保存相关记录的afterSave事件过程中发生任何错误,将抛出第一个发生的错误上的
yii\db\Exception
。错误信息将附加到拥有者模型的关系属性上。为了能够以用户友好的方式处理这些情况,必须捕获yii\db\Exception
异常。
当主模型被删除时删除相关记录
对于没有内置关系约束的DBMs,从1.5.0版本开始,现在可以指定在删除主模型时一起删除的关系。
为此,应将关系声明为具有将cascadeDelete
属性设置为true的属性。例如,当主模型将被删除时,相关的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)
:即使关系尚未被修改,也将关系标记为脏。