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

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

刻意练习,每日精进
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
最佳答案

大概看了一下源码,大致分析一下模型的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属性的.

5年前 评论
讨论数量: 4

大概看了一下源码,大致分析一下模型的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属性的.

5年前 评论

大概看了一下源码,大致分析一下模型的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属性的.

5年前 评论

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

5年前 评论

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

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!