1. 程式人生 > >tp5原始碼分析之資料庫查詢

tp5原始碼分析之資料庫查詢

1 查詢物件

查詢物件(Query)實現基本的查詢操作

與模型不同的是,查詢物件是在資料庫的整體上進行操作,所以需要指定資料庫表
模型針對的資料庫中的某個資料表的操作。

2 查詢操作

2-1 增刪改查

$query->insert()

public function insert(array $data, $replace = false, $getLastInsID = false, $sequence = null)
    {
        // 分析查詢表示式
        $options = $this->parseExpress();
        // 生成SQL語句
        $sql = $this
->builder()->insert($data, $options, $replace); // 獲取引數繫結 $bind = $this->getBind(); if ($options['fetch_sql']) { // 獲取實際執行的SQL語句 return $this->connection->getRealSql($sql, $bind); } // 執行操作 $result = $this->execute($sql, $bind); if
($getLastInsID) { $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); return $this->getLastInsID($sequence); } return $result; }

$query->insertAll()

public function insertAll(array $dataSet)
    {
        // 分析查詢表示式
        $options = $this
->parseExpress(); if (!is_array(reset($dataSet))) { return false; } // 生成SQL語句 $sql = $this->builder()->insertAll($dataSet, $options); // 獲取引數繫結 $bind = $this->getBind(); if ($options['fetch_sql']) { // 獲取實際執行的SQL語句 return $this->connection->getRealSql($sql, $bind); } else { // 執行操作 return $this->execute($sql, $bind); } }

$query->selectInsert()

public function selectInsert($fields, $table)
    {
        // 分析查詢表示式
        $options = $this->parseExpress();
        // 生成SQL語句
        $table = $this->parseSqlTable($table);
        $sql   = $this->builder()->selectInsert($fields, $table, $options);
        // 獲取引數繫結
        $bind = $this->getBind();
        if ($options['fetch_sql']) {
            // 獲取實際執行的SQL語句
            return $this->connection->getRealSql($sql, $bind);
        } else {
            // 執行操作
            return $this->execute($sql, $bind);
        }
    }

$query->select()

public function select($data = null)
    {
        if ($data instanceof Query) {
            return $data->select();
        } elseif ($data instanceof \Closure) {
            call_user_func_array($data, [ & $this]);
            $data = null;
        }
        // 分析查詢表示式
        $options = $this->parseExpress();

        if (false === $data) {
            // 用於子查詢 不查詢只返回SQL
            $options['fetch_sql'] = true;
        } elseif (!is_null($data)) {
            // 主鍵條件分析
            $this->parsePkWhere($data, $options);
        }

        $resultSet = false;
        if (empty($options['fetch_sql']) && !empty($options['cache'])) {
            // 判斷查詢快取
            $cache = $options['cache'];
            unset($options['cache']);
            $key       = is_string($cache['key']) ? $cache['key'] : md5(serialize($options));
            $resultSet = Cache::get($key);
        }
        if (!$resultSet) {
            // 生成查詢SQL
            $sql = $this->builder()->select($options);
            // 獲取引數繫結
            $bind = $this->getBind();
            if ($options['fetch_sql']) {
                // 獲取實際執行的SQL語句
                return $this->connection->getRealSql($sql, $bind);
            }
            // 執行查詢操作
            $resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_class']);

            if ($resultSet instanceof \PDOStatement) {
                // 返回PDOStatement物件
                return $resultSet;
            }

            if (isset($cache)) {
                // 快取資料集
                if (isset($cache['tag'])) {
                    Cache::tag($cache['tag'])->set($key, $resultSet, $cache['expire']);
                } else {
                    Cache::set($key, $resultSet, $cache['expire']);
                }
            }
        }

        // 返回結果處理
        if (count($resultSet) > 0) {
            // 資料列表讀取後的處理
            if (!empty($this->model)) {
                // 生成模型物件
                $model = $this->model;
                foreach ($resultSet as $key => $result) {
                    /** @var Model $result */
                    $result = new $model($result);
                    $result->isUpdate(true);
                    // 關聯查詢
                    if (!empty($options['relation'])) {
                        $result->relationQuery($options['relation']);
                    }
                    $resultSet[$key] = $result;
                }
                if (!empty($options['with']) && $result instanceof Model) {
                    // 預載入
                    $resultSet = $result->eagerlyResultSet($resultSet, $options['with'], is_object($resultSet) ? get_class($resultSet) : '');
                }
            }
        } elseif (!empty($options['fail'])) {
            $this->throwNotFound($options);
        }
        return $resultSet;
    }

$query->find()

public function find($data = null)
    {
        if ($data instanceof Query) {
            return $data->find();
        } elseif ($data instanceof \Closure) {
            call_user_func_array($data, [ & $this]);
            $data = null;
        }
        // 分析查詢表示式
        $options = $this->parseExpress();

        if (!is_null($data)) {
            // AR模式分析主鍵條件
            $this->parsePkWhere($data, $options);
        }

        $options['limit'] = 1;
        $result           = false;
        if (empty($options['fetch_sql']) && !empty($options['cache'])) {
            // 判斷查詢快取
            $cache = $options['cache'];
            if (true === $cache['key'] && !is_null($data) && !is_array($data)) {
                $key = 'think:' . $options['table'] . '|' . $data;
            } else {
                $key = is_string($cache['key']) ? $cache['key'] : md5(serialize($options));
            }
            $result = Cache::get($key);
        }
        if (!$result) {
            // 生成查詢SQL
            $sql = $this->builder()->select($options);
            // 獲取引數繫結
            $bind = $this->getBind();
            if ($options['fetch_sql']) {
                // 獲取實際執行的SQL語句
                return $this->connection->getRealSql($sql, $bind);
            }
            // 執行查詢
            $result = $this->query($sql, $bind, $options['master'], $options['fetch_class']);

            if ($result instanceof \PDOStatement) {
                // 返回PDOStatement物件
                return $result;
            }

            if (isset($cache)) {
                // 快取資料
                if (isset($cache['tag'])) {
                    Cache::tag($cache['tag'])->set($key, $result, $cache['expire']);
                } else {
                    Cache::set($key, $result, $cache['expire']);
                }
            }
        }

        // 資料處理
        if (!empty($result[0])) {
            $data = $result[0];
            if (!empty($this->model)) {
                // 返回模型物件
                $model = $this->model;
                $data  = new $model($data);
                $data->isUpdate(true, isset($options['where']['AND']) ? $options['where']['AND'] : null);
                if ($this->allowField) {
                    $data->allowField($this->allowField);
                }
                // 關聯查詢
                if (!empty($options['relation'])) {
                    $data->relationQuery($options['relation']);
                }
                if (!empty($options['with'])) {
                    // 預載入
                    $data->eagerlyResult($data, $options['with'], is_object($result) ? get_class($result) : '');
                }
            }
        } elseif (!empty($options['fail'])) {
            $this->throwNotFound($options);
        } else {
            $data = null;
        }
        return $data;
    }

$query->update()

public function update(array $data)
    {
        $options = $this->parseExpress();
        $pk      = $this->getPk($options);
        if (isset($options['cache']) && is_string($options['cache'])) {
            $key = $options['cache'];
        }

        if (empty($options['where'])) {
            // 如果存在主鍵資料 則自動作為更新條件
            if (is_string($pk) && isset($data[$pk])) {
                $where[$pk] = $data[$pk];
                if (!isset($key)) {
                    $key = 'think:' . $options['table'] . '|' . $data[$pk];
                }
                unset($data[$pk]);
            } elseif (is_array($pk)) {
                // 增加複合主鍵支援
                foreach ($pk as $field) {
                    if (isset($data[$field])) {
                        $where[$field] = $data[$field];
                    } else {
                        // 如果缺少複合主鍵資料則不執行
                        throw new Exception('miss complex primary data');
                    }
                    unset($data[$field]);
                }
            }
            if (!isset($where)) {
                // 如果沒有任何更新條件則不執行
                throw new Exception('miss update condition');
            } else {
                $options['where']['AND'] = $where;
            }
        } elseif (is_string($pk) && isset($options['where']['AND'][$pk]) && is_scalar($options['where']['AND'][$pk])) {
            $key = 'think:' . $options['table'] . '|' . $options['where']['AND'][$pk];
        }
        // 生成UPDATE SQL語句
        $sql = $this->builder()->update($data, $options);
        // 獲取引數繫結
        $bind = $this->getBind();
        if ($options['fetch_sql']) {
            // 獲取實際執行的SQL語句
            return $this->connection->getRealSql($sql, $bind);
        } else {
            // 檢測快取
            if (isset($key) && Cache::get($key)) {
                // 刪除快取
                Cache::rm($key);
            }
            // 執行操作
            return '' == $sql ? 0 : $this->execute($sql, $bind);
        }
    }

$query->delete()

public function delete($data = null)
    {
        // 分析查詢表示式
        $options = $this->parseExpress();
        if (isset($options['cache']) && is_string($options['cache'])) {
            $key = $options['cache'];
        }

        if (!is_null($data) && true !== $data) {
            if (!isset($key) && !is_array($data)) {
                // 快取標識
                $key = 'think:' . $options['table'] . '|' . $data;
            }
            // AR模式分析主鍵條件
            $this->parsePkWhere($data, $options);
        }

        if (true !== $data && empty($options['where'])) {
            // 如果條件為空 不進行刪除操作 除非設定 1=1
            throw new Exception('delete without condition');
        }
        // 生成刪除SQL語句
        $sql = $this->builder()->delete($options);
        // 獲取引數繫結
        $bind = $this->getBind();
        if ($options['fetch_sql']) {
            // 獲取實際執行的SQL語句
            return $this->connection->getRealSql($sql, $bind);
        }

        // 檢測快取
        if (isset($key) && Cache::get($key)) {
            // 刪除快取
            Cache::rm($key);
        }
        // 執行操作
        return $this->execute($sql, $bind);
    }

2-2 事務操作

$query->startTrans()

啟動事務

public function startTrans()
    {
        $this->connection->startTrans();
    }

$query->commit()

提交事務

public function commit()
    {
        $this->connection->commit();
    }

$query->transaction()

執行事務

public function transaction($callback)
   {
       return $this->connection->transaction($callback);
   }

$query->rollback()

回滾事務

public function rollback()
    {
        $this->connection->rollback();
    }

2-3 sql語句操作

$query->query()

呼叫聯結器的靜態query方法

public function query($sql, $bind = [], $master = false, $class = false)
    {
        return $this->connection->query($sql, $bind, $master, $class);
    }

$query->execute()

呼叫聯結器的靜態query方法

public function execute($sql, $bind = [])
    {
        return $this->connection->execute($sql, $bind);
    }

2-