借 Xdebug,助你分析 $model->save () 怎么执行的

   前言:之前我通过Laravel的测试用例分析过,但是只能算是会用,现在带大家看看它的原理

语句:update system_parameters set value = "omgzui" where id = 5
Laravel版本:5.5

超长代码警告 ⚠️

    // update `system_parameters` set `value` = "omgzui" where `id` = 5
    public function save(array $options = [])
    {
        // Get a new query builder instance for the connection.
        $query = $this->newModelQuery();
        // 执行失败就直接返回false
        if ($this->fireModelEvent('saving') === false) {
            return false;
        }
        // 已经存在就更新
        if ($this->exists) {
            $saved = $this->isDirty() ? $this->performUpdate($query) : true;
        }
        // 不存在就新增,按自增ID
        else {
            $saved = $this->performInsert($query);
            if (! $this->getConnectionName() &&
                $connection = $query->getConnection()) {
                $this->setConnection($connection->getName());
            }
        }
        // 成功之后完成一些附加操作
        if ($saved) {
            $this->finishSave($options);
        }
        return $saved;
    }

    // 获取数据库连接实例
    public function connection($name = null)
    {
        list($database, $type) = $this->parseConnectionName($name);
        $name = $name ?: $database;
        // 还没有连接实例就创建一个
        if (! isset($this->connections[$name])) {
            $this->connections[$name] = $this->configure(
                $this->makeConnection($database), $type
            );
        }
        return $this->connections[$name];
    }
    // 查询构建实例
    protected function newBaseQueryBuilder()
    {
        $conn = $this->getConnection();
        $grammar = $conn->getQueryGrammar();
        $builder = new QueryBuilder($conn, $grammar, $conn->getPostProcessor());
        if ($this->duplicateCache) {
            $builder->enableDuplicateCache();
        }
        return $builder;
    }
    // 通过比较原始数据和post数据返回改变的数据
    public function getDirty()
    {
        $dirty = [];
        foreach ($this->getAttributes() as $key => $value) {
            if (! $this->originalIsEquivalent($key, $value)) {
                $dirty[$key] = $value;
            }
        }
        return $dirty;
    }
    // 拼接sql
    // $boolean = "and"
    // $column = "id"
    // $operator = "="
    // $value = {int} 5
    public function where($column, $operator = null, $value = null, $boolean = 'and')
    {
        if ($column instanceof Closure) {
            $column($query = $this->model->newModelQuery());
            $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
        } else {
            $this->query->where(...func_get_args());
        }
        return $this;
    }
    // $this->query->where(...func_get_args());
    public function where($column, $operator = null, $value = null, $boolean = 'and')
    {
        // 处理字段数组
        if (is_array($column)) {
            return $this->addArrayOfWheres($column, $boolean);
        }
        // 处理操作符
        list($value, $operator) = $this->prepareValueAndOperator(
            $value, $operator, func_num_args() == 2
        );
        // 处理字段闭包
        if ($column instanceof Closure) {
            return $this->whereNested($column, $boolean);
        }
        // 给省略操作符的默认加上 "="
        if ($this->invalidOperator($operator)) {
            list($value, $operator) = [$operator, '='];
        }
        // 值是闭包。处理子查询
        if ($value instanceof Closure) {
            return $this->whereSub($column, $operator, $value, $boolean);
        }
        // 值为空,默认为null
        if (is_null($value)) {
            return $this->whereNull($column, $boolean, $operator !== '=');
        }
        // 处理字段是json格式
        if (Str::contains($column, '->') && is_bool($value)) {
            $value = new Expression($value ? 'true' : 'false');
        }
        // 简单查询whereBasic,主要是区分whereIn、whereNull这种
        $type = 'Basic';
        $this->wheres[] = compact(
            'type', 'column', 'operator', 'value', 'boolean'
        );
        if (! $value instanceof Expression) {
            $this->addBinding($value, 'where');
        }
        return $this;
    }
    // 更新
    public function update(array $values)
    {
        // $sql = rtrim("update {$table}{$joins} set $columns $where");
        // update `system_parameters` set `value` = ? where `id` = ?
        $sql = $this->grammar->compileUpdate($this, $values);
        return $this->connection->update($sql, $this->cleanBindings(
            $this->grammar->prepareBindingsForUpdate($this->bindings, $values)
        ));
    }
    // 多维数组变一维
    public static function flatten($array, $depth = INF)
    {
        $result = [];
        foreach ($array as $item) {
            $item = $item instanceof Collection ? $item->all() : $item;
            if (! is_array($item)) {
                $result[] = $item;
            } elseif ($depth === 1) {
                $result = array_merge($result, array_values($item));
            } else {
                $result = array_merge($result, static::flatten($item, $depth - 1));
            }
        }
        return $result;
    }
    // SQL最终执行
    // $bindings = {array} [2]
    // 0 = ""omgzui""
    // 1 = {int} 5
    // $query = "update `system_parameters` set `value` = ? where `id` = ?"
    public function affectingStatement($query, $bindings = [])
    {
        return $this->run($query, $bindings, function ($query, $bindings) {
            if ($this->pretending()) {
                return 0;
            }
            // 声明语句
            $statement = $this->getPdo()->prepare($query);
            // 语句预处理绑定
            $this->bindValues($statement, $this->prepareBindings($bindings));
            $statement->execute();
            // 修改了几条数据
            $this->recordsHaveBeenModified(
                ($count = $statement->rowCount()) > 0
            );
            return $count;
        });
    }
    protected function run($query, $bindings, Closure $callback)
    {
        $this->reconnectIfMissingConnection();
        $start = microtime(true);
        // 重连机制
        try {
            $result = $this->runQueryCallback($query, $bindings, $callback);
        } catch (QueryException $e) {
            $result = $this->handleQueryException(
                $e, $query, $bindings, $callback
            );
        }
        // 记录日志,同时广播
        $this->logQuery(
            $query, $bindings, $this->getElapsedTime($start)
        );
        return $result;
    }
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 1

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