内容简介: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的连接是orwhere{$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 编译器介绍
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Beginning iPhone and iPad Web Apps
Chris Apers、Daniel Paterson / Apress / 2010-12-15 / USD 39.99
It seems that everyone and her sister has developed an iPhone App—everyone except you, the hard-working web professional. And now with the introduction of the iPad, you may even feel farther behind. B......一起来看看 《Beginning iPhone and iPad Web Apps》 这本书的介绍吧!
在线进制转换器
各进制数互转换器
Markdown 在线编辑器
Markdown 在线编辑器