• yii2框架随笔32


    今天来看QueryBuilder.php,目录:db/QueryBuilder.php

    <?php
    /**
     * @link http://www.yiiframework.com/
     * @copyright Copyright (c) 2008 Yii Software LLC
     * @license http://www.yiiframework.com/license/
     */
    namespace yiidb;
    use yiibaseInvalidParamException;
    use yiibaseNotSupportedException;
    /**
     * QueryBuilder builds a SELECT SQL statement based on the specification given as a [[Query]] object.
     * QueryBuilder构建一个基于规范选择SQL语句作为一个[[Query]]对象。
     * QueryBuilder can also be used to build SQL statements such as INSERT, UPDATE, DELETE, CREATE TABLE,
     * from a [[Query]] object.
     * QueryBuilder也可以用来构建SQL语句,如插入、更新、删除、创建表。
     * @author Qiang Xue <qiang.xue@gmail.com>
     * @since 2.0
     */
    class QueryBuilder extends yiibaseObject
    {
        /**
         * The prefix for automatically generated query binding parameters.
         * 自动生成查询的前缀绑定参数
         */
        const PARAM_PREFIX = ':qp';
        /**
         * @var Connection the database connection.
         * 数据库连接。
         */
        public $db;
        /**
         * @var string the separator between different fragments of a SQL statement.
         * SQL语句的不同片段之间的分隔符。
         * Defaults to an empty space. This is mainly used by [[build()]] when generating a SQL statement.
         * 默认为一个空的空间。这主要使用[[build()]]时生成一个SQL语句。
         */
        public $separator = " ";
        /**
         * @var array the abstract column types mapped to physical column types.
         * 抽象的列类型映射到物理列类型。
         * This is mainly used to support creating/modifying tables using DB-independent data type specifications.
         * 这主要是用于支持使用DB-independent创建/修改表数据类型规范。
         * Child classes should override this property to declare supported type mappings.
         * 子类应该覆盖这个属性声明类型映射支持。
         */
        public $typeMap = [];
        /**
         * @var array map of query condition to builder methods.
         * 查询条件与构建方法的 map
         * These methods are used by [[buildCondition]] to build SQL conditions from array syntax.
         * 这些方法被[[buildCondition]]用来构建SQL条件数组的语法。
         */
        protected $conditionBuilders = [
            'NOT' => 'buildNotCondition',
            'AND' => 'buildAndCondition',
            'OR' => 'buildAndCondition',
            'BETWEEN' => 'buildBetweenCondition',
            'NOT BETWEEN' => 'buildBetweenCondition',
            'IN' => 'buildInCondition',
            'NOT IN' => 'buildInCondition',
            'LIKE' => 'buildLikeCondition',
            'NOT LIKE' => 'buildLikeCondition',
            'OR LIKE' => 'buildLikeCondition',
            'OR NOT LIKE' => 'buildLikeCondition',
            'EXISTS' => 'buildExistsCondition',
            'NOT EXISTS' => 'buildExistsCondition',
        ];
        /**
         * Constructor.
         * @param Connection $connection the database connection.
         * 连接数据库
         * @param array $config name-value pairs that will be used to initialize the object properties
         * 初始化对象属性
         */
        public function __construct($connection, $config = [])
        {
            $this->db = $connection;
            parent::__construct($config);
        }
        /**
         * Generates a SELECT SQL statement from a [[Query]] object.
         * 基于 Query 对象生成 SELECT SQL statement
         * @param Query $query the [[Query]] object from which the SQL statement will be generated.
         * 查询[[Query]]对象将生成的SQL语句
         * @param array $params the parameters to be bound to the generated SQL statement. These parameters will
         * be included in the result with the additional parameters generated during the query building process.
         * 参数被绑定到生成的SQL语句。这些参数将包括在结果与查询建筑过程中产生的附加参数.
         * @return array the generated SQL statement (the first array element) and the corresponding
         * parameters to be bound to the SQL statement (the second array element). The parameters returned
         * include those provided in `$params`.
         * 生成的SQL语句(第一个数组元素)和相应的参数绑定到SQL语句(第二个数组元素)。返回的参数
         *包括那些提供的“$params”。
         */
        public function build($query, $params = [])
        {
            $query = $query->prepare($this);
            // 将 query 中的参数与传入的参数 merge,以 query 中的参数 为主
            $params = empty($params) ? $query->params : array_merge($params, $query->params);
            $clauses = [
                // 构建 sql 中的 select 部分
                $this->buildSelect($query->select, $params, $query->distinct, $query->selectOption),
                // 构建 sql 中的 from 部分
                $this->buildFrom($query->from, $params),
                // 构建 sql 中的 join 部分
                $this->buildJoin($query->join, $params),
                // 构建 sql 中的 where 部分
                $this->buildWhere($query->where, $params),
                // 构建 sql 中的 groupBy 部分
                $this->buildGroupBy($query->groupBy),
                // 构建 sql 中的 having 部分
                $this->buildHaving($query->having, $params),
            ];
            $sql = implode($this->separator, array_filter($clauses));
            $sql = $this->buildOrderByAndLimit($sql, $query->orderBy, $query->limit, $query->offset);
            $union = $this->buildUnion($query->union, $params);
            if ($union !== '') {
                $sql = "($sql){$this->separator}$union";
            }
            return [$sql, $params];
        }
    
       /**
         * 根据数据组装 sql 中的 select 部分
         * @param array $columns
         * @param array $params the binding parameters to be populated
         * @param boolean $distinct
         * @param string $selectOption
         * @return string the SELECT clause built from [[Query::$select]].
         */
        public function buildSelect($columns, &$params, $distinct = false, $selectOption = null)
        {
            $select = $distinct ? 'SELECT DISTINCT' : 'SELECT';
            if ($selectOption !== null) {
                $select .= ' ' . $selectOption;
            }
            if (empty($columns)) {
                // 要选择的列为空,就拼上 *,筛选出所有的列
                return $select . ' *';
            }
            foreach ($columns as $i => $column) {
                if ($column instanceof Expression) {
                    // 如果该列继承自 Expression
                    if (is_int($i)) {
                        // 索引是 int,直接赋值
                        $columns[$i] = $column->expression;
                    } else {
                        // 否则,需要将索引 $i 作为别名
                        $columns[$i] = $column->expression . ' AS ' . $this->db->quoteColumnName($i);
                    }
                    // 合并参数
                    $params = array_merge($params, $column->params);
                } elseif ($column instanceof Query) {
                    // 如果该列继承自 Query
                    // 递归调用 build 方法,生成 sql
                    list($sql, $params) = $this->build($column, $params);
                    $columns[$i] = "($sql) AS " . $this->db->quoteColumnName($i);
                } elseif (is_string($i)) {
                    // 如果该列的索引是字符串
                    if (strpos($column, '(') === false) {
                        // 如果不存在 '(',就将 $column 作为字符串处理
                        $column = $this->db->quoteColumnName($column);
                    }
                    $columns[$i] = "$column AS " . $this->db->quoteColumnName($i);
                } elseif (strpos($column, '(') === false) {
                    // 如果该列不存在 '('
                    // 下面正则中的 ?i: 其实就是 ?:,只不过加上 i 可以忽略大小写
                    if (preg_match('/^(.*?)(?i:s+ass+|s+)([w-_.]+)$/', $column, $matches)) {
                        // 存在别名的情况
                        $columns[$i] = $this->db->quoteColumnName($matches[1]) . ' AS ' . $this->db->quoteColumnName($matches[2]);
                    } else {
                        // 不存在别名的情况
                        $columns[$i] = $this->db->quoteColumnName($column);
                    }
                }
            }
            // 将 $columns 用逗号拼接起来,作为要 select 的列
            return $select . ' ' . implode(', ', $columns);
        }
        /**
         * 根据数据组装 sql 中的 from 部分
         * @param array $tables
         * @param array $params the binding parameters to be populated
         * @return string the FROM clause built from [[Query::$from]].
         */
        public function buildFrom($tables, &$params)
        {
            if (empty($tables)) {
                return '';
            }
            $tables = $this->quoteTableNames($tables, $params);
            // 拼接 table 的名字
            return 'FROM ' . implode(', ', $tables);
        }
        /**
         * 根据数据组装 sql 中的 join 部分
         *
         * Join 内容的一个例子
         * ~~~
         * [
         *     ['INNER JOIN', 'user', 'user.id = author_id'],
         *     ['LEFT JOIN', 'team', 'team.id = team_id'],
         * ]
         *
         * @param array $joins
         * @param array $params the binding parameters to be populated
         * 被填充的绑定参数
         * @return string the JOIN clause built from [[Query::$join]].
         * 加入规则
         * @throws Exception if the $joins parameter is not in proper format
         * 连接参数中不和当地格式。
         */
        public function buildJoin($joins, &$params)
        {
            if (empty($joins)) {
                return '';
            }
            foreach ($joins as $i => $join) {
                if (!is_array($join) || !isset($join[0], $join[1])) {
                    throw new Exception('A join clause must be specified as an array of join type, join table, and optionally join condition.');
                }
                // 0:join type, 1:join table, 2:on-condition (optional)
                list ($joinType, $table) = $join;
                // 构建 table 名,处理它是一个 Query 实例或存在别名的情况,也可能存在函数
                $tables = $this->quoteTableNames((array) $table, $params);
                // 获取第一个为表名,其实就传入了一个,只不过返回值是数组
                $table = reset($tables);
                // 直接覆盖掉原来的值,但不会影响 $join 中的值,因为它是一份拷贝
                $joins[$i] = "$joinType $table";
                if (isset($join[2])) {
                    $condition = $this->buildCondition($join[2], $params);
                    if ($condition !== '') {
                        // 拼接 ON
                        $joins[$i] .= ' ON ' . $condition;
                    }
                }
            }
            return implode($this->separator, $joins);
        }
  • 相关阅读:
    JAVA的中文字符乱码问题
    JAVA SSH 框架介绍
    MySQL必备命令
    java文件操作
    注意事项
    再探java基础——throw与throws
    table与div互相嵌套注意
    数列F[19] + F[13]的值
    统计各类字符个数
    长理ACM 14-星期几(谌海军)
  • 原文地址:https://www.cnblogs.com/taokai/p/5502736.html
Copyright © 2020-2023  润新知