# 使用查询器

到目前为止,我们还没有真正的使用查询器,之前我们在DB facade上面调用了一些简单方法,让我们构建一些查询.

查询器将方法链接到一起从而构建查询，在链的末尾你可以使用一些方法(可能是get())来触发查询.

来让我们看一个简单的例子

```php
$usersOfType = DB::table('users')
        ->where('type', $type)
        ->get();
```

这里，我们创建了查询,查询users表格,查询条件是type,然后我们执行查询获取结果

让我们看下可用的链式方法有哪些,这些方法我称之为约束方法，修改方法，条件方法和结束/返回方法

**约束方法**

这些方法进行约束查询,并返回较小的数据子集

*select()*

选择查询的列

```php
$emails = DB::table('contacts')
    ->select('email', 'email2 as second_email')
    ->get();
// Or
$emails = DB::table('contacts')
    ->select('email')
    ->addSelect('email2 as second_email')
    ->get();
```

*where()*

你可以使用WHERE限制返回内容的范围，默认情况下,where()接收3个参数，列,比较运算符,值

```php
$newContacts = DB::table('contact')
            ->where('created_at', '>', now()->subDay())
            ->get();
```

然而，如果第二个运算符是=，那么可以省略这个参数

`$vipContacts = DB::table('contacts')->where('vip',true)->get();`

如果要组合where()语句,你可以再另外一个后面追加或者传递数组

```php
$newVips = DB::table('contacts')
            ->where('vip', true)
            ->where('created_at', '>', now()->subDay());
// Or
$newVips = DB::table('contacts')->where([['vip', true],
            ['created_at', '>', now()->subDay()],
        ]);
```

*orWhere()*

创建一个简单的OR WHERE

```php
$priorityContacts = DB::table('contacts')
            ->where('vip', true)
            ->orWhere('created_at', '>', now()->subDay())
            ->get();
```

若要创建具有多个条件的更复杂OR WHERE语句,请给orWhere传递闭包

```php
$contacts = DB::table('contacts') 
    ->where('vip', true)
    ->orWhere(function ($query) {
        $query->where('created_at', '>', now()->subDay())
         ->where('trial', false);
    }) 
    ->get();
```

> 多个where()和orWhere()混合调用
>
> 如果将orWhere()调用与多个where()调用结合使用，则需要非常小心，以确保查询按您认为的方式进行。这并不是因为Laravel有任何错误，而是因为像下面这样的查询可能无法实现您期望的结果：

```php
$canEdit = DB::table('users')
            ->where('admin', true)
            ->orWhere('plan', 'premium')
            ->where('is_plan_owner', true)
            ->get();

SELECT * FROM users 
    WHERE admin = 1
    OR plan = 'premium' 
    AND is_plan_owner = 1;
```

> 如果要编写SQL，上面的示例中明确指出“if this or（this and this）”，则需要将闭包传递到orWhere()中调用：

```php
$canEdit = DB::table('users')
            ->where('admin', true)
            ->orWhere(function ($query) {
                $query->where('plan', 'premium')
                    ->where('is_plan_owner', true);
            })
            ->get();
SELECT * FROM users 
    WHERE admin = 1
    OR (plan = 'premium' AND is_plan_owner = 1);
```

*whereBetween(colName, \[low, high])*

允许查询返回介于两个值之间的行(包含两个值)

```php
$mediumDrinks = DB::table('drinks')
            ->whereBetween('size', [6, 12])
            ->get();
```

同样适用于whereNotBetween(),但是结果会反转

*whereIn(colName, \[1, 2, 3])*

返回列表范围的行

```php
$closeBy = DB::table('contacts')
        ->whereIn('state', ['FL', 'GA', 'AL'])
        ->get();
```

同样适用于whereNotIn()，结果会反转

*whereNull(colName) 和 whereNotNull(colName)*

查询NULL或者Not NULL

*whereRaw()*

可以给where传递原生非转义字符串

```php
$goofs = DB::table('contacts')->whereRaw('id = 12345')->get()
```

> 小心SQL注入！
>
> 传递给whereraw()的任何SQL查询都不会被转义。小心使用此方法；这是应用程序中SQL注入攻击的主要机会。

*whereExists()*

允许你传递子查询,并至少返回一条记录,假设你想获取至少有一条留言的用户

```php
$commenters = DB::table('users')
            ->whereExists(function ($query) {
                $query->select('id')
                ->from('comments')
                ->whereRaw('comments.user_id = users.id');
            })
            ->get();
```

*distinct()*

返回与其他记录比较后的记录,通常与select()搭配使用,因为如果你使用了主键,则不会有重复的记录

```php
$lastNames = DB::table('contacts')->select('city')->distinct()->get();
```

**修改方法**

这些方法更改了查询结果,不仅仅是限制了结果

*orderBy(colName, direction)*

查询结果排序,第二个参数可以是asc（默认值，升序）或desc（降序）

```php
 $contacts = DB::table('contacts')
            ->orderBy('last_name', 'asc')
            ->get();
```

*groupBy() 和 having() 或者 havingRaw()*

将结果按列分组。或者，having（）和havingraw（）允许您根据组的属性筛选结果。例如，您可以只查找人口至少为30人的城市

```php
$populousCities = DB::table('contacts')
            ->groupBy('city')
            ->havingRaw('count(contact_id) > 30')
            ->get();
```

*skip()和take()*

最常用于分页，它们允许您定义返回多少行，以及在开始返回之前跳过多少行，就像分页系统中的页码和页面大小一样。

```php
// returns rows 31-40
$page4 = DB::table('contacts')->skip(30)->take(10)->get();
```

*latest(colName)和oldest(colName)*

通过传递的列进行升序(oldest)或者降序(latest)排列,如果没传值则按照created\_at排列

*inRandomOrder()*

随机排序结果

**条件方法**

Laravel 5.2以及更高版本有两个方法,可以通过传递值的布尔状态对内容进行条件判断（需要传递个闭包）

*when()*

第一个参数传"真"则会执行闭包内的查询,第一个参数传"假"那么什么都不做,注意第一个参数应该是布尔(true,false),一个可选值（$status，从用户输入中提取，并默认为null）或返回一个值的闭包,重要的是要返回true或者false,例如

```php
$status = request('status'); // Defaults to null if not set
$posts = DB::table('posts')
    ->when($status, function ($query) use ($status) {
        return $query->where('status', $status); })
    ->get();

$posts = DB::table('posts')
    ->when($ignoreDrafts, function ($query) { 
        return $query->where('draft', false);
    }) ->get();
```

您还可以传递第三个参数，即另一个闭包，只有在第一个参数*false*时才会应用该闭包。

*unless()*

与when相反,如果第一个参数为false,则会运行第二个闭包

**结束/返回方法**

终止查询链并触发SQL查询,如果在查询链没有调用这些方法,那么你将得到一个查询器实例，如果在查询链上执行了这些方法,那么你将得到具体的记录.

*get()*

获取查询器的所有结果

```php
$contacts = DB::table('contacts')->get();
$vipContacts = DB::table('contacts')->where('vip', true)->get();
```

*first()和firstOrFail()*

获取第一条结果,就像Like，但是它只返回一条

```php
$newestContact = DB::table('contacts')
            ->orderBy('created_at', 'desc')
            ->first();
```

如果没有结果，first()将自动失败，而firstorfail()将引发异常。

如果将列名数组传递给任一方法，它将只返回这些列的数据，而不是所有列的数据。

*find(id)和findOrFail(id)*

与first()类似,传递一个与主键对应的ID进行查找,如果ID对应的不存在,find会失败,而findOrFail会异常

```php
$contactFive = DB::table('contacts')->find(5);
```

*value()*

从第一行提取单个字段,像first()但是你只想获取单个列

```php
$newestContactEmail = DB::table('contacts')
            ->orderBy('created_at', 'desc')
            ->value('email');
```

*count()*

返回所有匹配结果的计数

```php
$countVips = DB::table('contacts')
    ->where('vip', true)
    ->count();
```

*min()和max()*

返回特定列的最小或最大值

```php
$highestCost = DB::table('orders')->max('amount');
```

*sum()和avg()*

返回特定列中所有值的总和或平均值

```php
$averageCost = DB::table('orders')
            ->where('status', 'completed')
            ->avg('amount');
```

**使用DB::raw在查询器中写原生查询**

之前看到过一些方法用于原生语法-例如select有一个对应的selectRaw方法可以给其传递字符串用于查询,然后放到WHERE语法后

当然你也可以调用DB::raw()来实现相同的结果

```php
$contacts = DB::table('contacts')
        ->select(DB::raw('*, (score * 100) AS integer_score'))
        ->get();
```

**连接**

定义联合查询有时有点困难,但是Laravel的查询器做的比较好,如例子

```php
$users = DB::table('users')
        ->join('contacts', 'users.id', '=', 'contacts.user_id')
        ->select('users.*', 'contacts.name', 'contacts.status')
        ->get();
```

join()创建了一个连接查询,你可以用链式写法组合多个join，或者使用leftJoin来创建左连接

最后，你也可以给join传递闭包创建更复杂的连接查询

```php
DB::table('users')
            ->join('contacts', function ($join) {
                $join
                    ->on('users.id', '=', 'contacts.user_id')
                    ->orOn('users.id', '=', 'contacts.proxy_user_id');
            })->get();
```

**组合**

通过首先创建两个查询，然后使用union()或unionall()方法，可以将它们联合起来（将它们的结果组合成一个结果）

```php
$first = DB::table('contacts')
        ->whereNull('first_name');
        
$contacts = DB::table('contacts')
    ->whereNull('last_name')
    ->union($first)
    ->get();
```

**插入**

插入非常简单,传递一个数组插入单条记录,传递数组的数组,插入多条记录,使用insertGetId()替代insert()可以自动生成主键

```php
$id = DB::table('contacts')->insertGetId([
        'name' => 'Abe Thomas',
        'email' => 'athomas1987@gmail.com',
]);
DB::table('contacts')->insert([
        ['name' => 'Tamika Johnson', 'email' => 'tamikaj@gmail.com'],
        ['name' => 'Jim Patterson', 'email' => 'james.patterson@hotmail.com'],
]);
```

**更新**

更新也非常简单,创建更新查询只需要将数组传递给它

```php
DB::table('contacts')
        ->where('points', '>', 100)
        ->update(['status' => 'vip']);
```

还可以使用increment()和decrement()方法快速增加和减少列。第一个参数是列名，第二个（可选）是要递增/递减的数字

```php
DB::table('contacts')->increment('tokens', 5);
DB::table('contacts')->decrement('tokens');
```

**删除**

删除更简单,在查询结尾调用delete()即可

```php
DB::table('users')
        ->where('last_login', '<', now()->subYear())
        ->delete();
```

您还可以截断表，该表将删除每一行并重置自增ID。

**JSON操作**

如果你有一个JSON列,你可以使用箭头语法来更新或选择JSON结构

```php
// Select all records where the "isAdmin" property of the "options"
// JSON column is set to true
DB::table('users')->where('options->isAdmin', true)->get();
        
// Update all records, setting the "verified" property
// of the "options" JSON column to true
DB::table('users')->update(['options->isVerified', true]);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://liseen315.gitbook.io/laravel/shu-ju-ku-he-eloquent/cha-xun-qi/shi-yong-cha-xun-qi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
