create () 和 save () 方法的底层原理是什么?
0

在laravel文档中看到了create和save操作数据。但是需要注意的是,fillable 与 guarded 只限制了 create 方法,而不会限制 save。
实例:
注意此时模型中的$fillable没加入user_id。
file
使用create报错,使用save是正常,不用管模型的fillable 与 guarded
file
但是我看了源码,没看懂。想知道有没有大神告知一下,从源码角度找出这个区别。

致力于成为laravel布道者

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
Flourishing
最佳答案

大概看了一下源码,大致分析一下模型的create方法最终通过__call魔术方法调用了Illuminate/Database/Eloquent/Builder.php里面的create方法:

    public function create(array $attributes = [])
    {
        return tap($this->newModelInstance($attributes), function ($instance) {
            $instance->save();
        });
    }

然后通过查看newModelInstance方法:

    public function newModelInstance($attributes = [])
    {
        return $this->model->newInstance($attributes)->setConnection(
            $this->query->getConnection()->getName()
        );
    }

然后再通过查看newInstance方法:

   public function newInstance($attributes = [], $exists = false)
    {
        $model = new static((array) $attributes);

        $model->exists = $exists;

        $model->setConnection(
            $this->getConnectionName()
        );

        return $model;
    }

注意这一句:

        $model = new static((array) $attributes);

实例化当前模型实例,传入了模型对应的属性数组.然后这句代码就会触发模型的__construct方法:

    public function __construct(array $attributes = [])
    {
        $this->bootIfNotBooted();

        $this->syncOriginal();

        $this->fill($attributes);
    }

注意查看fill这个方法,就是填充模型属性的,点开fill这个方法查看

$this->fill($attributes);

点开fill这个方法后,可以看见填充模型属性的时候调用了isFillable这个方法:

    public function fill(array $attributes)
    {
        $totallyGuarded = $this->totallyGuarded();

        foreach ($this->fillableFromArray($attributes) as $key => $value) {
            $key = $this->removeTableFromKey($key);
            if ($this->isFillable($key)) {
                $this->setAttribute($key, $value);
            } elseif ($totallyGuarded) {
                throw new MassAssignmentException($key);
            }
        }

        return $this;
    }

isFillable这个方法中做了对模型unguardedfillable的检查判断:

  public function isFillable($key)
    {
        if (static::$unguarded) {
            return true;
        }

        if (in_array($key, $this->getFillable())) {
            return true;
        }

        if ($this->isGuarded($key)) {
            return false;
        }

        return empty($this->getFillable()) &&
            ! Str::startsWith($key, '_');
    }

例如这段代码:

        if (in_array($key, $this->getFillable())) {
            return true;
        }

点开getFillable这个方法,可以看到如下代码:

    public function getFillable()
    {
        return $this->fillable;
    }

返回的就是模型的fillable属性数组,
看到这里就可以看到最终的实现了.得出的结论就是每次调用create方法都会检查模型的unguardedfillable属性的.

3周前
讨论数量: 4
Flourishing

大概看了一下源码,大致分析一下模型的create方法最终通过__call魔术方法调用了Illuminate/Database/Eloquent/Builder.php里面的create方法:

    public function create(array $attributes = [])
    {
        return tap($this->newModelInstance($attributes), function ($instance) {
            $instance->save();
        });
    }

然后通过查看newModelInstance方法:

    public function newModelInstance($attributes = [])
    {
        return $this->model->newInstance($attributes)->setConnection(
            $this->query->getConnection()->getName()
        );
    }

然后再通过查看newInstance方法:

   public function newInstance($attributes = [], $exists = false)
    {
        $model = new static((array) $attributes);

        $model->exists = $exists;

        $model->setConnection(
            $this->getConnectionName()
        );

        return $model;
    }

注意这一句:

        $model = new static((array) $attributes);

实例化当前模型实例,传入了模型对应的属性数组.然后这句代码就会触发模型的__construct方法:

    public function __construct(array $attributes = [])
    {
        $this->bootIfNotBooted();

        $this->syncOriginal();

        $this->fill($attributes);
    }

注意查看fill这个方法,就是填充模型属性的,点开fill这个方法查看

$this->fill($attributes);

点开fill这个方法后,可以看见填充模型属性的时候调用了isFillable这个方法:

    public function fill(array $attributes)
    {
        $totallyGuarded = $this->totallyGuarded();

        foreach ($this->fillableFromArray($attributes) as $key => $value) {
            $key = $this->removeTableFromKey($key);
            if ($this->isFillable($key)) {
                $this->setAttribute($key, $value);
            } elseif ($totallyGuarded) {
                throw new MassAssignmentException($key);
            }
        }

        return $this;
    }

isFillable这个方法中做了对模型unguardedfillable的检查判断:

  public function isFillable($key)
    {
        if (static::$unguarded) {
            return true;
        }

        if (in_array($key, $this->getFillable())) {
            return true;
        }

        if ($this->isGuarded($key)) {
            return false;
        }

        return empty($this->getFillable()) &&
            ! Str::startsWith($key, '_');
    }

例如这段代码:

        if (in_array($key, $this->getFillable())) {
            return true;
        }

点开getFillable这个方法,可以看到如下代码:

    public function getFillable()
    {
        return $this->fillable;
    }

返回的就是模型的fillable属性数组,
看到这里就可以看到最终的实现了.得出的结论就是每次调用create方法都会检查模型的unguardedfillable属性的.

3周前
等车的猪

@Flourishing 同学,分析的很透彻。。学到了,谢谢

3周前
等车的猪

总结一下,本问题的终极原因是查看fill()这个方法,fill()中进行了
fillable 与 guarded的判断。fill这个方法会去除传递的不符合模型中fillable 与 guarded设置的值,然后设置为空(只是去掉值,并不会删除这个字段),所以也可以后面重新赋值。

3周前
Flourishing

@等车的猪 嗯 我认为是这样的::grin:

3周前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!