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

OMGZui發表於2019-11-13

   前言:之前我通過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 協議》,轉載必須註明作者和本文連結

相關文章