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);
    }
原文地址:https://www.cnblogs.com/taokai/p/5502736.html