前言

最近在写底代码编程,写到关联关系保存的时候,想一下其实可以参考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;