laravel 基礎教程 —— 查詢生成器

數(shù)據(jù)庫:查詢生成器

簡介

Laravel 的數(shù)據(jù)庫查詢生成器提供了一種方便流利的接口來構(gòu)建和運行數(shù)據(jù)庫查詢。它可以用來在應用中提供執(zhí)行大多數(shù)的數(shù)據(jù)庫操作,并且它可以在所有支持的數(shù)據(jù)庫系統(tǒng)中進行協(xié)作。

注意:Laravel 的查詢生成器使用了 PDO 參數(shù)綁定來針對可能的 SQL 注入攻擊涝影。所以這里不需要手動的去清洗作為綁定所傳遞的字符串痹届。

檢索結(jié)果

從表中檢索所有的行

在開始流暢的執(zhí)行查詢之前,你需要先使用 DB 假面的 table 方法薇组,table 方法會返回一個給定表的查詢生成器實例,這允許你鏈式添加多種查詢約束坐儿,并最終獲取結(jié)果律胀。我們來看一個示例,我們只是調(diào)用 get 來獲取所有的表中的記錄:

<?php

namespace App\Http\Controllers;

use DB;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
  /**
   * Show a list of all of the application's users.
   *
   * @return Response
   */
   public function index()
   {
     $users = DB::table('users')->get();

     return view('user.index', ['users' => $users]);
   }
}

就像原生的查詢一樣貌矿,get 方法會返回一個 array 炭菌,其每一項結(jié)果都是一個 StdClass PHP 類的對象。你可以通過其屬性來訪問每一列的值:

foreach ($users as $user) {
  echo $user->name;
}

從表中檢索一個單獨的行或者列

如果你只需要從數(shù)據(jù)表中檢索出單獨的一行逛漫,你可以使用 first 方法黑低,該方法會返回一個單獨的 StdClass 對象:

$user = DB::table('users')->where('name', 'John')->first();

echo $user->name;

如果你甚至不需要一個完整的行,那么你可以通過使用 value 方法從記錄中獲取一個精確的列的值酌毡。該方法會直接返回列中的值:

$email = DB::table('users')->where('name', 'John')->value('email');

對結(jié)果進行分塊

如果你與表中的數(shù)千條記錄協(xié)作克握,你可以考慮使用 chunk 方法,該方法一次會從結(jié)果中檢索出一小塊枷踏。并且將這每一塊放進閉包中提供給進程菩暗。該方法對于編寫 Artisan 命令來協(xié)作數(shù)千條的記錄非常有用。比如呕寝,讓我們將整個用戶表分塊成每次 100 條來工作:

DB::table('users')->orderBy('id')->chunk(100, function ($users) {
  foreach ($users as $user) {
    //
  } 
});

你可以通過在 Closure 中返回 false 來停止進一步的處理塊的操作:

DB::table('users')->orderBy('id')->chunk(100, function ($users) {
  // Process the records...

  return false; 
});

檢索表列的值

如果你希望獲取一個數(shù)組來包含一個列中所有的值勋眯,你可以使用 pluck 方法,在這個例子中下梢,我們將獲取所有的角色名稱:

$titles = DB::table('roles')->pluck('title');

foreach($titles as $title) {
  echo $title;
}

你也可以為所返回的數(shù)組指定自定義的鍵:

$roles = DB::table('roles')->pluck('title', 'name');

foreach ($roles as $name => $title) {
  echo $title;
}

合計

查詢生成器也提供了多種合計的方法客蹋。比如 countmax孽江,min讶坯,avg,和 sum岗屏。你可以在查詢構(gòu)建后調(diào)用這些方法:

$users = DB::table('users')->count();

$price = DB::table('orders')->max('price');

當然辆琅,你可以結(jié)合其它約束來構(gòu)建你的查詢:

$price = DB::table('orders')
           ->where('finalized', 1)
           ->avg('price');

選擇

指定一個選擇約束

當然,你不會總是想要從數(shù)據(jù)庫中獲取所有的列这刷。你可以使用 select 方法來指定一個自定義的 select 約束到查詢:

$users = DB::table('users')->select('name', 'email as user_email')->get();

distinct 方法允許你強迫查詢返回一個不重復的結(jié)果:

$users = DB::table('users')->distinct()->get();

如果你已經(jīng)獲取了查詢生成器的實例婉烟,并且你想要在已經(jīng)添加過選擇約束的實例中添加額外一列的檢索,你可以使用 addSelect 方法:

$query = DB::table('users')->select('name');

$users = $query->addSelect('age')->get();

原生的表達式

有時候暇屋,你可能需要在查詢時使用原生的表達式似袁,這些表達式會以字符串的方式注入到查詢中,所以你應該小心的不要構(gòu)造出任何的 SQL 注入點。你可以使用 DB::raw 方法來構(gòu)造一個原生的表達式:

$users = DB::table('users')
           ->select(DB::raw('count(*) as user_count, status'))
           ->where('status', '<>', 1)
           ->groupBy('status')
           ->get();

Joins

內(nèi)連接聲明

查詢生成器也可以用來編寫 join 聲明昙衅。為了執(zhí)行一個基礎的 SQL 內(nèi)連接扬霜,你可以在查詢生成器中使用 join 方法。join 方法所接收的第一個參數(shù)應該是你需要加入的表的名稱而涉,而其它的參數(shù)則是對加入提供約束著瓶。當然,就如你所看到的啼县,你可以在一個查詢中加入多個表:

$users = DB::table('users')
           ->join('contacts', 'users.id', '=', 'contacts.user_id')
           ->join('orders', 'users.id', '=', 'orders.user_id')
           ->select('users.*', 'contacts.phone', 'orders.price')
           ->get();

左連接聲明

如果你需要執(zhí)行 left join 來代替 inner join材原,你可以使用 leftJoin 方法,leftJoin 方法具有和 join 方法相同的使用方法:

$users = DB::table('users')
           ->leftJoin('posts', 'users.id', '=', 'posts.user_id')
           ->get();

交叉連接聲明

你可以使用 crossJoin 方法來執(zhí)行一個交叉的連接操作季眷,你需要為 crossJoin 提供一個你想要交叉連接的表名华糖。交叉連接會生成第一個表和加入的表的笛卡爾積:

$users = DB::table('sizes')
           ->crossJoin('colours')
           ->get();

高級的 Join 聲明

你也可以指定更高級的 join 約束。你可以傳遞一個 Closure 作為 join 方法的第二個參數(shù)瘟裸。Closure 會接收一個 JoinClause 對象并且該對象允許你指定 join 約束:

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

如果你喜歡在 joins 中使用 where 風格約束,那么你可以在 join 上使用 whereorWhere 方法诵竭。該方法會比較列的值而取代直接比較兩列:

DB::table('users')
  ->join('contacts', function ($join) {
    $join->on('users.id', '=', 'contacts.user_id')
      ->where('contacts.user_id', '>', 5);
  })
  ->get();

Unions(聯(lián)合)

查詢生成器也提供了一種快捷的方式來將兩個查詢聯(lián)合起來话告。比如,你可以構(gòu)建一個初始化的查詢卵慰,然后使用 union 方法來將其與之后的查詢聯(lián)合起來:

$first = DB::table('users')
           ->whereNull('first_name');

$users = DB::table('users')
           ->whereNull('last_name')
           ->union($first)
           ->get();

unionAll 方法也是可用的沙郭,并且它與 union 方法具有相同的簽證方式。

Where 子句

簡單的 Where 子句

你可以在查詢生成器中使用 where 方法來在查詢中添加 where 約束病线。大多數(shù)基礎的 where 調(diào)用都需要提供三個參數(shù)鲤嫡,第一個參數(shù)是列的名稱,第二個參數(shù)是比較操作的方式暖眼,第三個參數(shù)則是針對列所需要評估的值。

比如诫肠,這里的查詢用來驗證 "votes" 列的值等于 100:

$users = DB::table('users')->where('votes', '=', 100)->get();

為了方便,你可以直接傳遞需要比較的值到第二個參數(shù)來表明列值與給定值相等的判斷:

$users = DB::table('users')->where('votes', 100)->get();

當然栋豫,你也可以在使用 where 子句時使用其它的操作符:

$users = DB::table('users')
           ->where('votes', '>=', 100)
           ->get();

$users = DB::table('users')
           ->where('votes', '<>', 100)
           ->get();

$users = DB::table('users')
           ->where('name', 'like', 'T%')
           ->get();

你也可以傳遞一個數(shù)組所組成的條件到 where 方法中:

$users = DB::table('users')->where([
  [status', '1'],
  ['subscribed','<>', '1'],
])->get();

或聲明

你可以將 where 約束連在一起挤安,以及添加 or 子句到查詢中。orWhere 方法接收和 where 方法相同的參數(shù):

$users = DB::table('users')
           ->where('votes', '>', 100)
           ->orWhere('name', 'John')
           ->get();

其它 Where 子句

whereBetween

whereBetween 方法確定列的值是否在兩個給定值之間:

$users = DB::table('users')
           ->whereBetween('votes', [1, 100])->get();

whereNotBetween

whereNotBetween 方法確定列的值應該不在所給定值區(qū)間內(nèi):

$users = DB::table('users')
           ->whereNotBetween('votes', [1, 100])
           ->get();

whereIn / whereNotIn

whereIn 方法驗證給定的列的值應該是給定的數(shù)組值中之一:

$users = DB::table('users')
           ->whereIn('id', [1, 2, 3])
           ->get();

whereNotIn 方法驗證所給定的列的值應該不在給定的數(shù)組中:

$users = DB::table('users')
           ->whereNotIn('id', [1, 2, 3])
           ->get();

whereNull / whereNotNull

whereNull 方法驗證所給定列的值應該是 NULL:

$users = DB::table('users')
           ->whereNull('updated_at')
           ->get();

whereNotNull 方法驗證所給定的列的值不應該為 NULL:

$users = DB::('users')
           ->whereNotNull('updated_at')
           ->get();

whereColumn

whereColumn 方法可以用來判斷兩列的值是否相等:

$users = DB::table('users')
           ->whereColumn('first_name', 'last_name');

你也可以傳遞一個比較符到方法中:

$users = DB::table('users')
           ->whereColumn('updated_at', '>', 'created_at');

whereColumn 方法可以接收一個包含了多個限制條件的數(shù)組丧鸯。這些條件會被使用 add 判定操作:

$users = DB::table('users')
           ->whereColumn([
             ['first_name', 'last_name'],
             ['updated_at', '>', 'created_at']
           ]);

高級 Where 子句

參數(shù)分組

有時候你需要構(gòu)建一些更加高級的 where 子句蛤铜,比如,“where exists”或者嵌套的參數(shù)分組。Laravel 的查詢生成器也能很好的處理這些昂羡。讓我們來看一個分組括號內(nèi)約束的例子:

DB::table('users')
  ->where('name', '=', 'John')
  ->orWhere(function ($query) {
    $query->where('votes', '>', 100)
          ->where('title', '<>', 'Admin');
  })
  ->get();

就如你所看到的絮记,傳遞一個 ClosureorWhere 方法來指導查詢生成器開始一個組約束。Closure 會接收一個查詢生成器的實例虐先,這樣你就可以在括號組內(nèi)構(gòu)建一些約束怨愤。上面的示例將會生成下面的 SQL:

select * from users where name = 'John' or (votes > 100 and title <> 'Admin')

Exists 聲明

whereExists 方法允許你編寫 where exists SQL 子句。whereExists 方法接收一個 Closure 參數(shù)蛹批,這個閉包會接收一個查詢生成器實例撰洗,這允許你在 "exists" 子句中去構(gòu)建查詢:

DB::table('users')
  ->whereExists(function ($query) {
    $query->select(DB::raw(1))
      ->from('orders')
      ->whereRaw('orders.user_id = users.id');
  })
  ->get();

上面的查詢將生成下面的 SQL:

select * from users
where exists (
  select 1 from orders where orders.user_id = users.id
)

JSON where 子句

Laravel 支持對支持 JSON 列類型的數(shù)據(jù)庫查詢 JSON 類型的列叶堆。目前呀袱,這只包含在 MySQL 5.7 和 Postgres。你需要使用 -> 操作符來查詢 JSON 列:

$users = DB::table('users')
           ->where('options->language', 'en')
           ->get();

$users = DB::table('users')
           ->where('preferences->dining->meal', 'salad')
           ->get();

排序帜消,分組猪勇,限制 & 位移

orderBy

orderBy 方法允許你對給定列的查詢結(jié)果進行排序设褐。orderBy 所接受的第一個參數(shù)應該是你希望進行排序的列,而第二個參數(shù)應該是你決定進行排序的方向泣刹,它們應該是 asc 或者 desc:

$users = DB::table('users')
           ->orderBy('name', 'desc')
           ->get();

inRandomOrder

inRandomOrder 方法可以用來對查詢的結(jié)果進行隨機排序助析。比如,你可以使用這個方法來隨機的獲取用戶:

$randomUser = DB::table('users')
                ->inRandomOrder()
                ->first();

groupBy / having / havingRaw

groupByhaving 方法可以用來對查詢結(jié)果進行分組椅您。having 方法具有和 where 方法相同的簽證:

$users = DB::table('users')
           ->groupBy('account_id')
           ->having('account_id', '>', 100)
           ->get();

havingRaw 方法可以用來在 having 子句中使用原生的字符串值外冀,比如,我們可以找到所有銷售額大于 $2,500 的部門:

$users = DB::table('orders')
           ->select('department', DB::raw('SUM(price) as total_sales'))
           ->groupBy('department')
           ->havingRaw('SUM(price) > 2500')
           ->get();

skip / take

你可以使用 takeskip 方法來限制查詢所返回結(jié)果的數(shù)量和在查詢中跳過一定的數(shù)目(offset):

$users = DB::table('users')->skip(10)->take(5)->get();

條件語句

有時候你可能希望只有當某些條件達成時才去添加一些查詢到語句中掀泳。又或者你想要在只有進入的請求數(shù)據(jù)中含有指定的字段時才會判斷在 where 子句中進行加入雪隧。你可以使用 when 方法來做到這些:

$role = $request->input('role');

$users = DB::table('users')
           ->where($role, function ($query) use ($role) {
             return $query->where('role_id', $role);
           })
           ->get();

when 方法只有所給定的第一個參數(shù)為 true 時才會執(zhí)行給定的閉包。如果第一個參數(shù)是 false员舵,那么閉包將不會被執(zhí)行固灵。

插入

查詢生成器也為插入記錄到數(shù)據(jù)庫中提供了 insert 方法。insert 方法可以接受列名所組成的鍵值對數(shù)組作為參數(shù):

DB::table('users')->insert(
  ['email' => 'john@example.com', 'votes' => 0]
);

你也可以在一次調(diào)用 insert 方法時添加多條記錄到數(shù)據(jù)庫中丛忆,你需要傳遞一個嵌套的數(shù)組熄诡,數(shù)組中的每一個數(shù)組都代表了數(shù)據(jù)庫中的一行:

DB::table('users')->insert([
  ['email' => 'taylor@example.com', 'votes' => 0],
  ['email' => 'dayle@example.com', 'votes' => 0]
]);

自動遞增的 IDs

如果挑中存在自動遞增的 id,你可以使用 insertGetId 方法來插入記錄的同時獲取 ID:

$id = DB::table('users')->insertGetId(
  ['email' => 'john@example.com', 'votes' => 0]
);

注意:當使用 PostgreSQL 時诗力,insertGetId 方法期望自增的列名為 id我抠。如果你想要檢索的 ID 是一個其他列名菜拓,你需要傳遞列名到 insertGetId 方法的第二個參數(shù)纳鼎。

更新

當然裳凸,除了新增記錄之外,查詢生成器也提供了為數(shù)據(jù)庫中存在的記錄更新的 update 方法逗宁。update 方法像 insert 方法一樣瞎颗,接收一個鍵值對所組成的數(shù)組來進行列值的更新言缤。你可以在使用 update 查詢時使用 where 子句:

DB::table('users')
  ->where('id', 1)
  ->update(['votes' => 1]);

增量 / 遞減

查詢生成器也提供一些方便的方法來對列的值進行增量和遞減。這只是一種快捷的方式轿曙,用來提供比手動寫入 update 語句更具表現(xiàn)力和簡潔的接口。

這兩種方法都接收最少一個參數(shù):待修改的列名守谓,第二個參數(shù)是一個可選的參數(shù)斋荞,它用來控制遞增或者遞減的數(shù)量:

DB::table('users')->increment('votes');

DB::table('users')->increment('votes', 5);

DB::table('users')->decrement('votes');

DB::table('users')->decrement('votes', 5);

你也可以指定同時更新額外的列:

DB::table('users')->increment('votes', 1, ['name' => 'John']);

刪除

當然平酿,查詢生成器也提供了從數(shù)據(jù)庫中刪除記錄的 delete 方法:

DB::table('users')->delete();

你可以在調(diào)用 delete 方法之前添加一些 where 子句:

DB::table('users')->where('votes', '>', 100)->delete();

如果你希望清除整個表的數(shù)據(jù)蜈彼,你可以使用 truncate 方法幸逆,它將清除所有的行并且重置自增的 ID 到 0:

DB::table('users')->truncate();

悲觀鎖

查詢生成器也提供了一些方法來幫助你在 select 聲明中實現(xiàn)“悲觀鎖”还绘。你可以在查詢中使用 sharedLock 方法來運行“共享鎖”。共享鎖可以保證所選定的行在事務提交前不會被修改:

DB::table('users')->where('votes', '>', 100)->sharedLock()->get();

另外抚太,你也可以使用 lockForUpdate 方法凭舶。更新鎖可以防止行被修改或者被另一共享鎖所選中:

DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末帅霜,一起剝皮案震驚了整個濱河市身冀,隨后出現(xiàn)的幾起案子搂根,更是在濱河造成了極大的恐慌剩愧,老刑警劉巖娇斩,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锦积,死亡現(xiàn)場離奇詭異歉嗓,居然都是意外死亡鉴分,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門家浇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钢悲,“玉大人,你說我怎么就攤上這事还棱≌涫郑” “怎么了琳要?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵稚补,是天一觀的道長课幕。 經(jīng)常有香客問我五垮,道長放仗,這世上最難降的妖魔是什么诞挨? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任亭姥,我火速辦了婚禮达罗,結(jié)果婚禮上粮揉,老公的妹妹穿的比我還像新娘扶认。我一直安慰自己辐宾,他們只是感情好,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布季研。 她就那樣靜靜地躺著与涡,像睡著了一般持偏。 火紅的嫁衣襯著肌膚如雪酌畜。 梳的紋絲不亂的頭發(fā)上谬莹,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天埠戳,我揣著相機與錄音蕉扮,去河邊找鬼喳钟。 笑死奔则,一個胖子當著我的面吹牛易茬,可吹牛的內(nèi)容都是我干的抽莱。 我是一名探鬼主播食铐,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼匕垫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了虐呻?” 一聲冷哼從身側(cè)響起象泵,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤寞秃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后单芜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜕该,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年洲鸠,在試婚紗的時候發(fā)現(xiàn)自己被綠了堂淡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖费薄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤竞慢,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布败潦,位于F島的核電站意狠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏遮晚。R本人自食惡果不足惜汹族,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一守问、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦探越、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽箫措。三九已至镀岛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捻悯。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人餐塘。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像不翩,于是被迫代替她去往敵國和親亚皂。 傳聞我的和親對象是個殘疾皇子禁漓,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容