内容简介:Laravel Database——查询构造器与语法编译器源码分析 (上)
前言
本文 GitBook 地址: https://www.gitbook.com/book/leoyang90/laravel-source-analysis
在前两个文章中,我们分析了数据库的连接启动与数据库底层 CRUD
的原理,底层数据库服务支持原生 sql
的运行。本文以 mysql
为例,向大家讲述支持 Fluent
的查询构造器 query
与语法编译器 grammer
的原理。
DB::table 与 查询构造器
若是不想使用原生的 sql
语句,我们可以使用 DB::table
语句,该语句会返回一个 query
对象:
public function table($table)
{
return $this->query()->from($table);
}
public function query()
{
return new QueryBuilder(
$this, $this->getQueryGrammar(), $this->getPostProcessor()
);
}
我们可以看到,query
会有两个成员,queryGrammar
与 postProcessor
。queryGrammar
负责对 QueryBuilcder
的结果进行 sql
语言的转化,postProcessor
负责查询结果的后处理。
之所以 laravel
推荐我们使用查询构造器,而不是原生的 sql
,原因在于可以避免 sql
注入漏洞。当然,我们也可以在使用 DB::select()
函数中手动写 bindings
的值,但是这样的话,我们写 sql
的语句是就必须是这样:
DB::select('select * from table where col=?',[1]);
必然会带来很多不便。
有了查询构造器,我们就可以写出 fluent
类型的语句:
DB::table('table')->select('*')->where('col', 1);
是不是很方便?
CRUD 与语法编译器
相应于 connection
对象的 CRUD
,语法编译器有 compileInsert
、compileSelect
、compileUpdate
、compileDelete
。其中最重要的是 compileSelect
,因为它不仅负责了 select
语句的语法编译,还负责聚合语句 aggregate
、from
语句、join
连接语句、wheres
条件语句、groups
分组语句、havings
条件语句、orders
排序语句、limit
语句、offset
语句、unions
联合语句、lock
语句:
protected $selectComponents = [
'aggregate',
'columns',
'from',
'joins',
'wheres',
'groups',
'havings',
'orders',
'limit',
'offset',
'unions',
'lock',
];
public function compileSelect(Builder $query)
{
$original = $query->columns;
if (is_null($query->columns)) {
$query->columns = ['*'];
}
$sql = trim($this->concatenate(
$this->compileComponents($query))
);
$query->columns = $original;
return $sql;
}
protected function compileComponents(Builder $query)
{
$sql = [];
foreach ($this->selectComponents as $component) {
if (! is_null($query->$component)) {
$method = 'compile'.ucfirst($component);
$sql[$component] = $this->$method($query, $query->$component);
}
}
return $sql;
}
可以看出来,语法编译器会将上述所有的语句放入 $sql[]
成员中,然后通过 concatenate
函数组装成 sql
语句:
protected function concatenate($segments)
{
return implode(' ', array_filter($segments, function ($value) {
return (string) $value !== '';
}));
}
wrap 函数
若想要了解语法编译器,我们就必须先要了解 grammer
中一个重要的函数 wrap
,这个函数专门对表名与列名进行处理,
public function wrap($value, $prefixAlias = false)
{
if ($this->isExpression($value)) {
return $this->getValue($value);
}
if (strpos(strtolower($value), ' as ') !== false) {
return $this->wrapAliasedValue($value, $prefixAlias);
}
return $this->wrapSegments(explode('.', $value));
}
处理的流程:
- 若是
Expression
对象,利用函数getValue
直接取出对象值,不对其进行任何处理,用于处理原生sql
。expression
对象的作用是保护原始参数,避免框架解析的一种方式。也就是说,当我们用了expression
来包装参数的话,laravel
将不会对其进行任何处理,包括库名解析、表名前缀、别名等。 - 若表名/列名存在
as
,则利用函数wrapAliasedValue
为表名设置别名。 - 若表名/列名含有
.
,则会被分解为库名/表名
,或者表名/列名
,并调用函数wrapSegments
。
wrapAliasedValue 函数
wrapAliasedValue
函数用于处理别名:
protected function wrapAliasedValue($value, $prefixAlias = false)
{
$segments = preg_split('/\s+as\s+/i', $value);
if ($prefixAlias) {
$segments[1] = $this->tablePrefix.$segments[1];
}
return $this->wrap(
$segments[0]).' as '.$this->wrapValue($segments[1]
);
}
可以看到,首先程序会根据 as
将字符串分为两部分,as
前的部分递归调用 wrap
函数,as
后的部分调用 wrapValue
函数.
wrapValue 函数
wrapValue
函数用来处理添加符号 "
,例如 table
,会被这个函数变为 "table"
。需要注意的是 table1"table2
这种情况,假如我们的数据库中存在一个表,名字就叫做: table1"table2
,我们在数据库查询的时候,必须将表名转化为 "table1""table2"
,只有这样,数据库才会有效地转化表名为 "table1"table2"
,否则数据库就会报告错误:找不到表。
protected function wrapValue($value)
{
if ($value !== '*') {
return '"'.str_replace('"', '""', $value).'"';
}
return $value;
}
wrapSegments 函数
wrapSegments
函数会判断当前参数,如果是 table.column
,会将前一部分 table
调用 wrapTable
, column
调用 wrapValue
,最后生成 “table”."column"
。
protected function wrapSegments($segments)
{
return collect($segments)->map(function ($segment, $key) use ($segments) {
return $key == 0 && count($segments) > 1
? $this->wrapTable($segment)
: $this->wrapValue($segment);
})->implode('.');
}
wrapTable 函数
wrapTable
函数用于为数据表添加表前缀:
public function wrapTable($table)
{
if (! $this->isExpression($table)) {
return $this->wrap($this->tablePrefix.$table, true);
}
return $this->getValue($table);
}
wrap
整体流程图如下:
from 语句
我们看到 DB::table
实际上是调用了查询构造器的 from
函数。接下来我们就看看,当我们写下了
DB::table('table')->get()
时发生了什么。
public function from($table)
{
$this->from = $table;
return $this;
}
我们看到,from
函数极其简单,我们接下来看 get
:
public function get($columns = ['*'])
{
$original = $this->columns;
if (is_null($original)) {
$this->columns = $columns;
}
$results = $this->processor->processSelect($this, $this->runSelect());
$this->columns = $original;
return collect($results);
}
laravel
的查询构造器是懒加载的,只有调用了 get
函数才会真正的调用语法编译器,采用调用底层 connection
对象进行数据库查询:
protected function runSelect()
{
return $this->connection->select(
$this->toSql(), $this->getBindings(), ! $this->useWritePdo
);
}
public function toSql()
{
return $this->grammar->compileSelect($this);
}
compileFrom 函数
首先我们先看看流程图:
语法编译器 grammer
对于 from
语句的处理由函数 compileFrom
负责:
protected function compileFrom(Builder $query, $table)
{
return 'from '.$this->wrapTable($table);
}
从流程图可以看出,具体流程与 wrap
类似。
我们调用 from
时,可以传递两种参数,一种是字符串,另一种是 expression
对象:
DB::table('table');
DB::table(new Expression('table'));
- 传递
expression
对象
当我们传递 expression
对象的时候,grammer
就会调用 getValue
取出原生 sql
语句。
- 传递字符串
当我们向 from
传递普通的字符串时,laravel
就会对字符串调用 wrap
函数进行处理,处理流程上一个小节已经说明:
- 为表名加上前缀
$this->tablePrefix
- 若字符串存在
as
,则为表名设置别名。 - 若字符串含有
.
,则会被分解为库名
与表名
,并进行分别调用wrapTable
函数与wrapValue
进行处理。 - 为表名前后添加
"
,例如t1.t2
会被转化为"t1"."t2"
(不同的数据库添加的字符不同,mysql 就不是"
)
laravel
对 from
处理流程存在一些问题,表名前缀设置功能与数据库名功能公用存在问题,相关 issue
地址是:[Bug] Table prefix added to database name when using database.table,有任何兴趣的同学可以在这个 issue
里面讨论,或者直接向作者提 PR
。若作者对此部分有任何修改,我会同步修改这篇文章。
Select 语句
本小节会介绍 select
语句:
public function select($columns = ['*'])
{
$this->columns = is_array($columns) ? $columns : func_get_args();
return $this;
}
queryBuilder
的 select
语句很简单,我们不多讨论.
selectRaw
:
public function selectRaw($expression, array $bindings = [])
{
$this->addSelect(new Expression($expression));
if ($bindings) {
$this->addBinding($bindings, 'select');
}
return $this;
}
public function addSelect($column)
{
$column = is_array($column) ? $column : func_get_args();
$this->columns = array_merge((array) $this->columns, $column);
return $this;
}
可以看到, selectRaw
就是将 Expression
对象赋值到 columns
中,我们在前面说到,框架不会对 Expression
进行任何处理(更准确的说是 wrap
函数),这样就保证了原生语句的执行。
我们接着看 grammer
.
compileColumns 函数
protected function compileColumns(Builder $query, $columns)
{
if (! is_null($query->aggregate)) {
return;
}
$select = $query->distinct ? 'select distinct ' : 'select ';
return $select.$this->columnize($columns);
}
public function columnize(array $columns)
{
return implode(', ', array_map([$this, 'wrap'], $columns));
}
可以看到,grammer
对 select
的语法编译调用 wrap
函数对每个 select
的字段进行处理,处理过程在上面详解过,在此不再赘述。
selectSub 语句
所谓的 select
子查询,就是查询的字段来源于其他数据表。对于这种查询,可以分成两部来理解,首先忽略整个select子查询,查出第一个表中的数据,然后根据第一个表的数据执行子查询,
laravel
的 selectSub
支持闭包函数、queryBuild
对象或者原生 sql
语句,以下是单元测试样例:
$query = DB::table('one')->select(['foo', 'bar'])->where('key', '=', 'val');
$query->selectSub(function ($query) {
$query->from('two')->select('baz')->where('subkey', '=', 'subval');
}, 'sub');
另一种写法:
$query = DB::table('one')->select(['foo', 'bar'])->where('key', '=', 'val');
$query_sub = DB::table('one')->select('baz')->where('subkey', '=', 'subval');
$query->selectSub($query_sub, 'sub');
生成的 sql
:
select "foo", "bar", (select "baz" from "two" where "subkey" = 'subval') as "sub" from "one" where "key" = 'val'
selectSub
语句的实现比较简单:
public function selectSub($query, $as)
{
if ($query instanceof Closure) {
$callback = $query;
$callback($query = $this->forSubQuery());
}
list($query, $bindings) = $this->parseSubSelect($query);
return $this->selectRaw(
'('.$query.') as '.$this->grammar->wrap($as), $bindings
);
}
protected function parseSubSelect($query)
{
if ($query instanceof self) {
$query->columns = [$query->columns[0]];
return [$query->toSql(), $query->getBindings()];
} elseif (is_string($query)) {
return [$query, []];
} else {
throw new InvalidArgumentException;
}
}
可以看到,如果 selectSub
的参数是闭包函数,那么就会先执行闭包函数,闭包函数将会为 query
根据查询语句更新对象。
parseSubSelect
函数为子查询解析 sql
语句与 binding
变量。
where 语句总结
在 laravel
文档中,queryBuild
的用法很详尽,但是为了更好的理解源码,我们在这里再次大概的总结一下:
基础用法
users = DB::table('users')->where('votes', '=', 100)->get();
$users = DB::table('users')->where('votes', 100)->get();
这两个是等价的写法。
where
数组
$users = DB::table('users')->where(
['status' => 1, 'subscribed' => 1],
)->get();
$users = DB::table('users')->where([
['status', '1'],
['subscribed', '1'],
])->get();
$users = DB::table('users')->where([
['status', '1'],
['subscribed', '<>', '1'],
])->get();
where
查询组
DB::table('users')
->where('name', '=', 'John')
->orWhere(function ($query) {
$query->where('votes', '>', 100)
->where('title', '<>', 'Admin');
})
->get();
这一句的 sql
语句是
select * from users where name = 'John' or (votes > 100 and title <> 'Admin')
where
子查询
public function testFullSubSelects()
{
$builder = $this->getBuilder();
DB::table('users')
->Where('id', '=', function ($q) {
$q->select(new Raw('max(id)'))->from('users')->where('email', '=', 'bar');
});
}
这一句的 sql
语句是
select * from "users" where "email" = foo or "id" = (select max(id) from "users" where "email" = bar`
orWhere
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere('name', 'John')
->get();
whereDate / whereMonth / whereDay / whereYear / whereTime
$users = DB::table('users')
->whereDate('created_at', '2016-12-31')
->get();
$users = DB::table('users')
->whereMonth('created_at', '12')
->get();
$users = DB::table('users')
->whereDay('created_at', '31')
->get();
$users = DB::table('users')
->whereYear('created_at', '2016')
->get();
$users = DB::table('users')
->whereTime('created_at', '>=', '22:00')
->get();
whereBetween / whereNotBetween
$users = DB::table('users')
->whereBetween('votes', [1, 100])->get();
$users = DB::table('users')
->whereNotBetween('votes', [1, 100])
->get();
whereRaw / orWhereRaw
$users = DB::table('users')
->whereRaw('id = ? or email = ?', [1, 'foo'])
->get();
$users = DB::table('users')
->orWhereRaw('id = ? or email = ?', [1, 'foo'])
->get();
whereIn / whereNotIn / orWhereIn / orWhereNotIn
$users = DB::table('users')
->whereIn('id', [1, 2, 3])
->get();
$users = DB::table('users')
->whereNotIn('id', [1, 2, 3])
->get();
$users = DB::table('users')
->whereIn('id', function ($q) {
$q->select('id')->from('users')->where('age', '>', 25)->take(3);
});
$users = DB::table('users')
->whereNotIn('id', function ($q) {
$q->select('id')->from('users')->where('age', '>', 25)->take(3);
});
$query = DB::table('users')->select('id')->where('age', '>', 25)->take(3);
$users = DB::table('users')->whereIn('id', $query);
$users = DB::table('users')->whereNotIn('id', $query);
有意思的是,当我们在 whereIn / whereNotIn / orWhereIn / orWhereNotIn
中传入空数组的时候:
$users = DB::table('users')
->whereIn('id', [])
->get();
$users = DB::table('users')
->orWhereIn('id', [])
->get();
这个时候,框架自动会生成如下的 sql
:
select * from "users" where 0 = 1;
select * from "users" where "id" = ? or 0 = 1
whereColumn
$users = DB::table('users')
->whereColumn('first_name', 'last_name')
->get();
$users = DB::table('users')
->whereColumn('updated_at', '>', 'created_at')
->get();
$users = DB::table('users')
->whereColumn([
['first_name', '=', 'last_name'],
['updated_at', '>', 'created_at']
])->get();
whereNull / whereNotNull / orWhereNull / orWhereNotNull
$users = DB::table('users')
->whereNull('updated_at')
->get();
$users = DB::table('users')
->whereNotNull('updated_at')
->get();
$users = DB::table('users')
->orWhereNull('updated_at')
->get();
$users = DB::table('users')
->orWhereNotNull('updated_at')
->get();
whereExists / whereNotExists / orWhereExists / orWhereNotExists
DB::table('users')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
// select * from users where exists ( select 1 from orders where orders.user_id = users.id)
DB::table('users')
->whereNotExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
// select * from users where not exists ( select 1 from orders where orders.user_id = users.id)
DB::table('users')
->orWhereExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
// select * from users or exists ( select 1 from orders where orders.user_id = users.id)
DB::table('users')
->orWhereNotExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
// select * from users or not exists ( select 1 from orders where orders.user_id = users.id)
where 函数
我们首先先看看源码:
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);
}
if (is_null($value)) {
return $this->whereNull($column, $boolean, $operator !== '=');
}
if (Str::contains($column, '->') && is_bool($value)) {
$value = new Expression($value ? 'true' : 'false');
}
$type = 'Basic';
$this->wheres[] = compact(
'type', 'column', 'operator', 'value', 'boolean'
);
if (! $value instanceof Expression) {
$this->addBinding($value, 'where');
}
return $this;
}
可以看到,为了支持框架的多种 where
形式,where
的代码中写了很多的条件语句。我们接下来一个个分析。
grammer——compileWheres 函数
在此之前,我们先看看语法编译器对 where
查询的处理:
protected function compileWheres(Builder $query)
{
if (is_null($query->wheres)) {
return '';
}
if (count($sql = $this->compileWheresToArray($query)) > 0) {
return $this->concatenateWhereClauses($query, $sql);
}
return '';
}
compileWheres
函数负责所有 where
查询条件的语法编译工作,compileWheresToArray
函数负责循环编译查询条件,concatenateWhereClauses
函数负责将多个查询条件合并。
protected function compileWheresToArray($query)
{
return collect($query->wheres)->map(function ($where) use ($query) {
return $where['boolean'].' '.$this->{"where{$where['type']}"}($query, $where);
})->all();
}
protected function concatenateWhereClauses($query, $sql)
{
$conjunction = $query instanceof JoinClause ? 'on' : 'where';
return $conjunction.' '.$this->removeLeadingBoolean(implode(' ', $sql));
}
compileWheresToArray
函数负责把 $query->wheres
中多个 where
条件循环起来:
$where['boolean']
是多个查询条件的连接,and
或者or
,一般where
条件默认为and
,各种orWhere
的连接是or
where{$where['type']}
是查询的类型,laravel
把查询条件分为以下几类:base
、raw
、in
、notIn
、inSub
、notInSub
、null
、notNull
、between
、column
、nested
、sub
、exist
、notExist
。每种类型的查询条件都有对应的grammer
方法
concatenateWhereClauses
函数负责连接所有的搜索条件,由于 join
的连接条件也会调用 compileWheres
函数,所以会有判断是否是真正的 where
查询,
where 数组
如果 column
是数组的话,就会调用:
protected function addArrayOfWheres($column, $boolean, $method = 'where')
{
return $this->whereNested(function ($query) use ($column, $method, $boolean) {
foreach ($column as $key => $value) {
if (is_numeric($key) && is_array($value)) {
$query->{$method}(...array_values($value));
} else {
$query->$method($key, '=', $value, $boolean);
}
}
}, $boolean);
}
可以看到,数组分为两类,一种是列名为 key
,例如 ['foo' => 1, 'bar' => 2]
,这个时候就是调用 query->where('foo', '=', '1', ‘and’)
。还有一种是 [['foo','1'],['bar','2']]
,这个时候就会调用 $query->where(['foo','1'])
。
public function whereNested(Closure $callback, $boolean = 'and')
{
call_user_func($callback, $query = $this->forNestedWhere());
return $this->addNestedWhereQuery($query, $boolean);
}
public function addNestedWhereQuery($query, $boolean = 'and')
{
if (count($query->wheres)) {
$type = 'Nested';
$this->wheres[] = compact('type', 'query', 'boolean');
$this->addBinding($query->getBindings(), 'where');
}
return $this;
}
grammer——whereNested
语法编译器中负责查询组的函数是 whereNested
,它会取出 where
中的 query
,递归调用 compileWheres
函数
protected function whereNested(Builder $query, $where)
{
$offset = $query instanceof JoinClause ? 3 : 6;
return '('.substr($this->compileWheres($where['query']), $offset).')';
}
由于 compileWheres
会返回 where ...
或者 on ...
等开头的 sql
语句,所以我们需要把返回结果截取前3个字符或6个字符。
where 查询组
若查询条件是一个闭包函数,也就是第一个参数 column
是个闭包函数,那么就要调用 whereNested
函数,过程和上述过程一致。
whereSub 子查询
如果第二个参数或者第三个参数是一个闭包函数的话,就是 where
子查询语句,这时需要调用 whereSub
函数:
protected function whereSub($column, $operator, Closure $callback, $boolean)
{
$type = 'Sub';
call_user_func($callback, $query = $this->forSubQuery());
$this->wheres[] = compact(
'type', 'column', 'operator', 'query', 'boolean'
);
$this->addBinding($query->getBindings(), 'where');
return $this;
}
grammer——whereSub
grammer
中负责子查询的是 whereSub
函数:
protected function whereSub(Builder $query, $where)
{
$select = $this->compileSelect($where['query']);
return $this->wrap($where['column']).' '.$where['operator']." ($select)";
}
因为子查询中可以存在 select
、where
、join
等一切 sql
语句,所以递归的是 compileSelect
这个大的函数,而不是仅仅 compileWheres
。
whereNull 语句
whereNull
函数也很简单:
public function whereNull($column, $boolean = 'and', $not = false)
{
$type = $not ? 'NotNull' : 'Null';
$this->wheres[] = compact('type', 'column', 'boolean');
return $this;
}
grammer——whereNull 函数
protected function whereNull(Builder $query, $where)
{
return $this->wrap($where['column']).' is null';
}
whereBasic 语句
如果上述情况都不符合,那么就是最基础的 where
语句,类型是 basic
.
$type = 'Basic';
$this->wheres[] = compact(
'type', 'column', 'operator', 'value', 'boolean'
);
if (! $value instanceof Expression) {
$this->addBinding($value, 'where');
}
grammer——whereBasic 函数
grammer
中最基础的 where
语句由 wherebasic
函数负责:
protected function whereBasic(Builder $query, $where)
{
$value = $this->parameter($where['value']);
return $this->wrap($where['column']).' '.$where['operator'].' '.$value;
}
public function parameter($value)
{
return $this->isExpression($value) ? $this->getValue($value) : '?';
}
wherebasic
函数对参数进行了替换,利用 ?
来替换真正的值。
orWhere 语句
orWhere
函数只是在 where
函数的基础上固定了最后一个参数:
public function orWhere($column, $operator = null, $value = null)
{
return $this->where($column, $operator, $value, 'or');
}
whereColumn 语句
whereColumn
函数是简化版的 where
函数,只是 where
类型不是 basic
,而是 column
:
public function whereColumn($first, $operator = null, $second = null, $boolean = 'and')
{
if (is_array($first)) {
return $this->addArrayOfWheres($first, $boolean, 'whereColumn');
}
if ($this->invalidOperator($operator)) {
list($second, $operator) = [$operator, '='];
}
$type = 'Column';
$this->wheres[] = compact(
'type', 'first', 'operator', 'second', 'boolean'
);
return $this;
}
grammer——whereColumn
可以看到 whereColumn
与 whereBasic
的区别是对 value
的不同处理,whereBasic
实际上是将其看作值,需要用 ?
来替换,参数加载到 binding
中去的。而 whereColumn
是将 second
当做列名来处理,是需要经过表名、别名等处理的:
protected function whereColumn(Builder $query, $where)
{
return $this->wrap($where['first']).' '.$where['operator'].' '.$this->wrap($where['second']);
}
whereIn 语句
public function whereIn($column, $values, $boolean = 'and', $not = false)
{
$type = $not ? 'NotIn' : 'In';
if ($values instanceof EloquentBuilder) {
$values = $values->getQuery();
}
if ($values instanceof self) {
return $this->whereInExistingQuery(
$column, $values, $boolean, $not
);
}
if ($values instanceof Closure) {
return $this->whereInSub($column, $values, $boolean, $not);
}
if ($values instanceof Arrayable) {
$values = $values->toArray();
}
$this->wheres[] = compact('type', 'column', 'values', 'boolean');
foreach ($values as $value) {
if (! $value instanceof Expression) {
$this->addBinding($value, 'where');
}
}
return $this;
}
可以看出来,whereIn
支持四种参数: EloquentBuilder
、queryBuilder
、Closure
、Arrayable
。
whereInExistingQuery 函数
当 whereIn
第二个参数是 queryBuild
时,就会调用 whereInExistingQuery
函数:
protected function whereInExistingQuery($column, $query, $boolean, $not)
{
$type = $not ? 'NotInSub' : 'InSub';
$this->wheres[] = compact('type', 'column', 'query', 'boolean');
$this->addBinding($query->getBindings(), 'where');
return $this;
}
可以看出,这个函数添加了一个类型为 InSub
或 NotInSub
类型的 where
,我们接着在语法编译器来看:
grammer——whereInSub / whereNotInSub
whereInSub
/ whereNotInSub
与 whereSub
类似,只是 operator
被固定成 in
/ not in
而已:
protected function whereInSub(Builder $query, $where)
{
return $this->wrap($where['column']).' in ('.$this->compileSelect($where['query']).')';
}
protected function whereNotInSub(Builder $query, $where)
{
return $this->wrap($where['column']).' not in ('.$this->compileSelect($where['query']).')';
}
whereInSub 函数
当 whereIn
第二个参数是闭包函数的时候,就会调用 whereInSub
函数:
protected function whereInSub($column, Closure $callback, $boolean, $not)
{
$type = $not ? 'NotInSub' : 'InSub';
call_user_func($callback, $query = $this->forSubQuery());
$this->wheres[] = compact('type', 'column', 'query', 'boolean');
$this->addBinding($query->getBindings(), 'where');
return $this;
}
可以看出来,除了闭包函数需要执行获得 query
对象之外,whereInSub
函数与 whereInExistingQuery
函数一致。
In / NotIn 类型 where
如果参数传递了数组,那么就会创建 In
或者 NotIn
类型的 where
:
$this->wheres[] = compact('type', 'column', 'values', 'boolean');
foreach ($values as $value) {
if (! $value instanceof Expression) {
$this->addBinding($value, 'where');
}
}
grammer——whereIn / whereNotIn
whereIn
或者 whereNotIn
与 whereBasic
函数基本一致,只是参数值需要循环调用 parameter
函数:
protected function whereIn(Builder $query, $where)
{
if (! empty($where['values'])) {
return $this->wrap($where['column']).' in ('.$this->parameterize($where['values']).')';
}
return '0 = 1';
}
protected function whereNotIn(Builder $query, $where)
{
if (! empty($where['values'])) {
return $this->wrap($where['column']).' not in ('.$this->parameterize($where['values']).')';
}
return '1 = 1';
}
public function parameterize(array $values)
{
return implode(', ', array_map([$this, 'parameter'], $values));
}
whereBetween 语句
类似的 whereBetween
创建了新的 between
类型的 where
:
public function whereBetween($column, array $values, $boolean = 'and', $not = false)
{
$type = 'between';
$this->wheres[] = compact('column', 'type', 'boolean', 'not');
$this->addBinding($values, 'where');
return $this;
}
grammer——whereBetween
有意思的是,whereBetween
不支持原生的参数值,也就是不支持 expression
对象,所以直接用 ?
来代替参数值:
protected function whereBetween(Builder $query, $where)
{
$between = $where['not'] ? 'not between' : 'between';
return $this->wrap($where['column']).' '.$between.' ? and ?';
}
whereExist 语句
whereExist
只支持闭包函数作为子查询语句,和之前一样,创建了 Exist
或者 NotExist
类型的 where
public function whereExists(Closure $callback, $boolean = 'and', $not = false)
{
$query = $this->forSubQuery();
call_user_func($callback, $query);
return $this->addWhereExistsQuery($query, $boolean, $not);
}
public function addWhereExistsQuery(Builder $query, $boolean = 'and', $not = false)
{
$type = $not ? 'NotExists' : 'Exists';
$this->wheres[] = compact('type', 'operator', 'query', 'boolean');
$this->addBinding($query->getBindings(), 'where');
return $this;
}
grammer——whereExists / whereNotExists
可以看到,这部分的语法编译器仍然和 whereSub
非常类似:
protected function whereExists(Builder $query, $where)
{
return 'exists ('.$this->compileSelect($where['query']).')';
}
protected function whereNotExists(Builder $query, $where)
{
return 'not exists ('.$this->compileSelect($where['query']).')';
}
whereDate / whereMonth / whereDay / whereYear / whereTime
关于时间的查询条件都由函数 addDateBasedWhere
负责创建新的类型的 where
,由于这些函数大致相同,我这里只贴一种:
protected function addDateBasedWhere($type, $column, $operator, $value, $boolean = 'and')
{
$this->wheres[] = compact('column', 'type', 'boolean', 'operator', 'value');
$this->addBinding($value, 'where');
return $this;
}
public function whereDate($column, $operator, $value = null, $boolean = 'and')
{
list($value, $operator) = $this->prepareValueAndOperator(
$value, $operator, func_num_args() == 2
);
return $this->addDateBasedWhere('Date', $column, $operator, $value, $boolean);
}
...
grammer——dateBasedWhere
时间查询条件都是利用数据库的 date
、month
、day
、year
、time
函数实现的:
protected function whereTime(Builder $query, $where)
{
return $this->dateBasedWhere('time', $query, $where);
}
protected function dateBasedWhere($type, Builder $query, $where)
{
$value = $this->parameter($where['value']);
return $type.'('.$this->wrap($where['column']).') '.$where['operator'].' '.$value;
}
whereRaw 语句
原生的 sql
语句及其简单:
public function whereRaw($sql, $bindings = [], $boolean = 'and')
{
$this->wheres[] = ['type' => 'raw', 'sql' => $sql, 'boolean' => $boolean];
$this->addBinding((array) $bindings, 'where');
return $this;
}
语法编译器工作:
protected function whereRaw(Builder $query, $where)
{
return $where['sql'];
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Laravel Database——查询构造器与语法编译器源码分析 (下)
- Laravel Database——查询构造器与语法编译器源码分析 (中)
- Java类 静态代码块、构造代码块、构造函数初始化顺序
- TS 的构造签名和构造函数类型是啥?傻傻分不清楚
- LLVM接受NVIDIA的“f18” Fortran编译器作为官方Fortran编译器
- Go 编译器介绍
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Art of Computer Programming, Volumes 1-3 Boxed Set
Donald E. Knuth / Addison-Wesley Professional / 1998-10-15 / USD 199.99
This multivolume work is widely recognized as the definitive description of classical computer science. The first three volumes have for decades been an invaluable resource in programming theory and p......一起来看看 《The Art of Computer Programming, Volumes 1-3 Boxed Set》 这本书的介绍吧!