前言
最近在写底代码编程,写到关联关系保存的时候,想一下其实可以参考Laravel-admin 关联保存,因为他很简单的通过 ->hasMany 一个函数就解决了平常我们写的麻烦的关联模型。所以别人优秀的代码和思想是值得借鉴的。
查找源码位置
关联保存肯定是在Form 模块中编写的,所以我很快的定位到了 vendor\encore\laravel-admin\src\Form.php
在update() 方法中写了关联模型的数据保存
先 通过预加载的方式,把关联的模型的数据加载出来,
$this->model = $builder->with($this->getRelations())->findOrFail($id);
$this->setFieldOriginalValue();
验证数据,如果报错则结束
$validationMessages = $this->validationMessages($data)
保存数据
DB::transaction(function () {
$updates = $this->prepareUpdate($this->updates);
foreach ($updates as $column => $value) {
/* @var Model $this ->model */
$this->model->setAttribute($column, $value);
}
$this->model->save();
$this->updateRelation($this->relations);
});
先保存主表的数据,再更新关联数据
/**
* Update relation data.
*
* @param array $relationsData
*
* @return void
*/
protected function updateRelation($relationsData)
{
foreach ($relationsData as $name => $values) {
if (!method_exists($this->model, $name)) {
continue;
}
$relation = $this->model->$name();
$oneToOneRelation = $relation instanceof Relations\HasOne
|| $relation instanceof Relations\MorphOne
|| $relation instanceof Relations\BelongsTo;
$prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
if (empty($prepared)) {
continue;
}
switch (true) {
case $relation instanceof Relations\BelongsToMany:
case $relation instanceof Relations\MorphToMany:
if (isset($prepared[$name])) {
$relation->sync($prepared[$name]);
}
break;
case $relation instanceof Relations\HasOne:
case $relation instanceof Relations\MorphOne:
$related = $this->model->getRelationValue($name) ?: $relation->getRelated();
foreach ($prepared[$name] as $column => $value) {
$related->setAttribute($column, $value);
}
// save child
$relation->save($related);
break;
case $relation instanceof Relations\BelongsTo:
case $relation instanceof Relations\MorphTo:
$related = $this->model->getRelationValue($name) ?: $relation->getRelated();
foreach ($prepared[$name] as $column => $value) {
$related->setAttribute($column, $value);
}
// save parent
$related->save();
// save child (self)
$relation->associate($related)->save();
break;
case $relation instanceof Relations\HasMany:
case $relation instanceof Relations\MorphMany:
foreach ($prepared[$name] as $related) {
/** @var Relations\HasOneOrMany $relation */
$relation = $this->model->$name();
$keyName = $relation->getRelated()->getKeyName();
/** @var Model $child */
$child = $relation->findOrNew(Arr::get($related, $keyName));
if (Arr::get($related, static::REMOVE_FLAG_NAME) == 1) {
$child->delete();
continue;
}
Arr::forget($related, static::REMOVE_FLAG_NAME);
$child->fill($related);
$child->save();
}
break;
}
}
}
通过模型调用,可以查询这个关联是用了哪种关联模型
$relation = $this->model->$name();
$oneToOneRelation = $relation instanceof Relations\HasOne
|| $relation instanceof Relations\MorphOne
|| $relation instanceof Relations\BelongsTo;
BelongsToMany 和 MorphToMany 方式保存方式:
case $relation instanceof Relations\BelongsToMany:
case $relation instanceof Relations\MorphToMany:
if (isset($prepared[$name])) {
$relation->sync($prepared[$name]);
}
break;
HasOne 和MorphOne 保存方式:
case $relation instanceof Relations\HasOne:
case $relation instanceof Relations\MorphOne:
$related = $this->model->getRelationValue($name) ?: $relation->getRelated();
foreach ($prepared[$name] as $column => $value) {
$related->setAttribute($column, $value);
}
// save child
$relation->save($related);
BelongsTo 和 MorphTo 方式:
case $relation instanceof Relations\BelongsTo:
case $relation instanceof Relations\MorphTo:
$related = $this->model->getRelationValue($name) ?: $relation->getRelated();
foreach ($prepared[$name] as $column => $value) {
$related->setAttribute($column, $value);
}
// save parent
$related->save();
// save child (self)
$relation->associate($related)->save();
break;
HasMany 和 MorphMany 保存方式
case $relation instanceof Relations\HasMany:
case $relation instanceof Relations\MorphMany:
foreach ($prepared[$name] as $related) {
/** @var Relations\HasOneOrMany $relation */
$relation = $this->model->$name();
$keyName = $relation->getRelated()->getKeyName();
/** @var Model $child */
$child = $relation->findOrNew(Arr::get($related, $keyName));
if (Arr::get($related, static::REMOVE_FLAG_NAME) == 1) {
$child->delete();
continue;
}
Arr::forget($related, static::REMOVE_FLAG_NAME);
$child->fill($related);
$child->save();
}
break;