【源码分析】Laravel-admin 关联保存的原理

邵弘伟
2023-12-01

前言

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