九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、Laravelの提供する機能で特に便利なのが「Query Builder」、つまりデータベースの操作といっていいと思います。
Laravelに限らずですが、フレームワークがなかった頃はいちいちDBに接続するコードを実行し、それから冗長なSQL文を繰り返し記述したものですけど、現在はおかげさまでホントにすっきりしたコードで開発できるようになりました。
ということで、今回はLaravelの根本的な機能のDB操作の中から、データ取得にフォーカスした全実例を紹介します。
【動作環境】
- Laravel 5.6
- MySQL 5.7
目次
- 1 データ取得の基本
- 2 カラム(フィールド)を指定する
- 3 リレーション先のデータを取得する
- 4 該当するデータだけ取得する(検索)
- 5 並び順を指定する
- 6 グループ化
- 7 skip()で取得する位置を指定する
- 8 take()でデータベースから取得する件数を指定する
- 9 when()でSQLクエリーを条件分岐させる
- 10 集約したデータを取得する
- 11 データが存在しているかどうかをチェックする
- 12 SQL文をそのまま指定する
- 13 テーブルを結合させる
- 14 union()で2つのテーブルデータを1つにする
- 15 ページごとにデータを取得する(Pagination)
- 16 ソフトデリート(論理削除)されたデータを取得する
- 17 データが見つからない場合は考慮したデータ取得
- 18 おわりに
データ取得の基本
get()で全てのデータを取得する
Laravelでデータベースからデータ取得する基本は以下のようになります。
$items = \DB::table('items')->get(); // 全てのデータが取得できる
データベース名をtable()内に書き込んでget()を呼ぶだけ。これで全データをデータベースから引っ張ってくることができます。とても簡単ですね。
そして、取得したデータをひとつずつ取り出したい場合はループを使います。
foreach ($items as $item) { echo $item->name; // 各データの名前を表示 }
また、よりシンプルに書きたい場合は、Eloquent(モデル)を作っておくと、いちいちDB::table()を使わなくとも以下のように書くことができます。
$items = \App\Item::get(); // 全てのデータが取得できる
ちなみに、\Appというのはネームスペースなので、「use App\Item;」を追加しておくと、以下のようによりシンプルにコードを書くこともできます。
use App\Item; class HomeController extends Controller { public function index() { $items = Item::get(); }
※ちなみにall()もget()と同じです。
first()でひとつだけデータ取得する
get()は該当するデータ全てを取得するやり方でしたが、first()を使えばひとつだけデータ取得することができます。
例えば、以下のようにした場合はどうなるでしょうか。
$item = \App\Item::first();
この場合は以下のように、たくさん取得されるデータの中から最初のものだけが取得されることになります。
Array ( [id] => 1 [name] => 名前0 [created_at] => 2018-07-02 17:21:50 [updated_at] => 2018-07-02 17:21:50 )
私の環境では並べ替え指定がされていない場合、「id」の昇順となりますので、今回は「id」が1番目のデータが取得されました。
find()でidを指定して取得
Laravelではデータベースのテーブルに「id」というカラムが用意されていることが前提になっています。
これを利用した便利な書き方がfind()です。つまり、id番号を指定するだけでget()やfirst()を使わなくてもデータが取得できます。
やり方はこうです。
$id = 1; $item = \App\Item::find($id);
find()を使うと後で説明するwhere()を使わなくてよくなります。そのためコード量が少なくなるので、特別な理由がない場合はこちらを使うことをおすすめします。
また、idは複数指定することもできます。この場合は複数データが取得されますので、foreach()などを使ってループさせながらデータ取得するといいでしょう。
$items = \App\Item::find([1, 2]);
chunk()である件数ごとにデータ取得
例えば、10件ごとにユーザー・データを取得したい場合はchunk()を使うと簡単にできます。
\App\User::chunk(10, function($users){ print_r($users->toArray()); // 最大10件のユーザーデータ });
取得されたデータ(ここでは$users)はforeach()でループすることができます。
また、もし途中で終了したい場合は以下のように「return false;」を追加してあげましょう。
\App\User::chunk(1, function($users){ foreach ($users as $user) { if($user->id == 1) { return false; } } });
カラム(フィールド)を指定する
select()で指定
「id」や「name」といったカラムを指定したい場合はselect()を使います。
では、「name」だけを取得する場合を見てみましょう。
$items = \App\Item::select('name')->get();
取得されるデータはこうなります。「name」だけが取得できていますね。
Array ( [0] => Array ( [name] => 名前0 ) [1] => Array ( [name] => 名前1 ) [2] => Array ( [name] => 名前2 ) )
では、複数カラムを取得する場合はどうすればいいでしょうか。
そのまま第2引数、第3引数にカラムを追加していってください。
$items = \App\Item::select('id', 'name', 'created_at')->get();
これを実行すると中身はこうなります。
Array ( [0] => Array ( [id] => 1 [name] => 名前0 [created_at] => 2018-07-02 17:07:17 ) [1] => Array ( [id] => 2 [name] => 名前1 [created_at] => 2018-07-02 17:07:17 ) [2] => Array ( [id] => 3 [name] => 名前2 [created_at] => 2018-07-02 17:07:17 ) )
※ちなみにCakePHPなどでは、カラムが複数になると配列にしないといけなかったりしますが、Laravelでは引数を追加するだけなので、よりシンプルに使えると思います。昔はarray()と書いていたので余計に可読性が悪くなっていたのを覚えています。
ちなみに、後からカラムを追加することもできます。その場合はaddSelect()を使いましょう。
$query = \App\Item::select('id'); $query->addSelect('name'); $items = $query->get();
このテクニックを使えば、GETやPOSTパラメータの内容によって取得するカラムを変更することもできるでしょう。
カラム名を変更する
例えば、「name」というキーを「customer」に変更したい場合です。
この場合は、select内に「as」をつけて記述します。
実際の例はこうなります。
$items = \App\Item::select('name as customer')->get();
実際の取得データは以下のようになります。
Array ( [0] => Array ( [customer] => 名前0 ) [1] => Array ( [customer] => 名前1 ) [2] => Array ( [customer] => 名前2 ) )
value()で1つのデータだけを取得する
例えば、データベースに設定情報を保存していて「サイトの所有者情報」を取得したいといった、たった1つだけデータ取得したい場合、value()を使えばシンプルに実装できます。
実際の例を見てみましょう。
$owner = \App\Configuration::where('key', 'owner')->value('owner');
ただし、注意が必要なのは複数のデータがある場合は「1つめの」データからだけしか取得ができないということです。
例えば、以下は2人のユーザーが取得できる例ですがvalue()で取得できるのは最初のデータの名前(name)だけです。
$name = \App\User::whereIn('id', [1, 2])->value('name');
複数のデータが必要な場合は次のpluck()を使いましょう。
pluck()で特定のデータを複数取得する
value()はたった1つだけのデータしか取得できませんでしたが、配列のようにデータを複数取得する方法もあります。それがpluck()です。
例を見てみましょう。
$names = \App\User::pluck('name'); foreach ($names as $name) { echo $name; // 各データの名前 }
この例は、「name」のデータだけを複数取得するコードです。
取得したデータは通常の配列のようにforeach()で取得ができます。
また、連想配列のようにキーをつけて取得することもできて、その場合は第2引数にキーにしたいカラムを指定します。
実際の例はこうなります。
$names = \App\User::pluck('name', 'id'); foreach ($names as $id => $name) { echo $id; // User ID echo $name; // 各データの名前 }
同じく各データを取り出す場合はforeach()が使えますが、さっきと違うのはキー(ここでは$id)も同時に取得できるという部分です。
リレーション先のデータを取得する
with()でリレーション先のデータを一緒に取得する
例えば、あるユーザーのデータを取得する場合に以下のようなリレーションしているテーブルの内容も一緒に取得する方法です。
- 1 対 1
- 1 対 多
- 多 対 多
実際の例を見てみましょう。
$users = \App\User::with('contacts')->get();
with()を使って以下のようなモデル内に指定したリレーションを指定しています。
public function contacts() { return $this->hasMany('App\Contact'); }
また、リレーション先のテーブルに条件をつけたい場合は以下のように配列にしてfunction()内でクエリを作成することができます。
$users = \App\User::with(['contacts' => function($q){ $q->where('created_at', '>', '2018-07-01'); }])->get();
※後で出てくるwhereHas()と似ていますが内容は全く違っています。with()の場合はリレーション先にデータがなくてもユーザー・データは取得されます。それに対して、whereHas()ではリレーション先にデータがないユーザーは取得されません。
該当するデータだけ取得する(検索)
where()で指定する
もし、カラム「name」が「名前1」というデータを取得したい場合はwhere()を使って該当データだけを探しだします。
$item = \App\Item::where('name', '名前1')->first();
ちなみに、「=」をつけた以下のやり方も結果は同じですがコードは少ないほうがいいので、何か特別な理由がない場合は上の省略バージョンをおすすめします。
$item = \App\Item::where('name', '=', '名前1')->first();
AND/OR検索
「->」でつなげてAND検索
例えば、以下のAND条件の場合です。
- ID番号は1番
- 名前は鈴木
実際の例はこうなります。
$user = \App\User::where('id', 1) ->where('name', '鈴木') ->first();
AND検索は場合は、where()の後にまたアロー演算子「->」を使ってさらにwhere()を追加すればOKです。
orWhere()でOR検索
OR検索したい場合は、同じく「->」を使ってorWhere()をつなげることで実現できます。
実際の例を見てみましょう。
$user = \App\User::where('id', 1) ->orWhere('name', '鈴木') ->get();
この場合、以下の条件のどちらかに該当するデータを取得することができます。(もちろんどちらも該当する場合でも取得できます)
- idが1
- nameが「鈴木」
LIKEであいまい検索
SQL文には「LIKE」を使ってあいまい検索をすることもできます。例えば、以下のような場合です。
- 「鈴木」で始まる名前のデータだけ取得する
- 「子」で終わる名前のデータだけ取得する
- 名前に「川」が入っている全データを取得
ひとつずつ見ていきましょう。
特定の文字列から始まるデータの取得
では名前が「鈴木」で始まるデータだけを取得してみましょう。
$users = \App\User::where('name', 'like', '名前%')->get();
ワイルドカード(どんな文字列でもOKにできる文字。トランプのジョーカーみたいなものですね)は「%」で、MySQLなどと同じです。
特定の文字列で終わるデータの取得
もちろん逆もできます。
次に「子」で終わる名前だけ取得してみましょう。例えば「花子」や「景子」などです。
$users = \App\User::where('name', 'like', '%子')->get();
今回はワイルドカードが先頭にきています。
特定の文字列が含まれている全データを取得
では、とにかく「川」という文字が名前のどこかに入っているデータだけを取得する場合はどうすればいいでしょうか。
答えは、こうなります。
$users = \App\User::where('name', 'like', '%川%')->get();
ワイルドカードを2つ使っているんですね。これで「川」が名前に入っているデータを取得することができます。
「大きい」「少ない」「同じ」で比較して検索
次に数字や日付などで以下のような条件で検索する場合をみていきましょう。
- これより大きい(後)
- これより小さい(前)
- これと同じ
- これ以外
- これ以上(同じか大きい)
- これ以下(同じか少ない)
やり方は簡単で比較演算子をwhere()に追加するだけです。
「>」でより大きい(後の日付)データだけ取得
$items = \App\Item::where('id', '>', 1)->get();
「<」より小さい(前)データだけ取得
$items = \App\Item::where('id', '<', 3)->get();
「=」で同じデータだけ取得
$items = \App\Item::where('id', '=', 3)->get();
※ただし、イコールの場合は省略できるので以下の方がコードが少なくていいでしょう。
$items = \App\Item::where('id', 3)->get();
「!=」で特定のデータ「以外」のものを取得
$items = \App\Item::where('id', '!=', 1)->get();
※ちなみに、「以外」の指定方法は「<>」も使えます。(たしか、こちらのほうがより対応しているDBが多かったはずです)
$items = \App\Item::where('id', '<>', 1)->get();
「>=」で以上(同じか大きい)のデータだけ取得
$items = \App\Item::where('id', '>=', 2)->get();
「<=」で以下(同じか少ない)のデータだけ取得
$items = \App\Item::where('id', '<=', 2)->get();
複数の条件を一気に指定
ちなみに、where()は多重配列を指定することで複数条件を一度に指定することができます。
$users = \App\User::where([ ['id', 2], ['name', 'like', '鈴木%'] ])->get();
もちろん、「like」や「>=」などの比較演算子も使えます。
function()を使った複雑なwhere()
実は、where()の引数はfunction()を受け付けてくれて、これを使えば複雑なSQL文をつくることができます。
例えば、以下のようなAND検索とOR検索が混ざっているような場合です。
「名前が鈴木から始まるデータで、なおかつ年齢が20以下もしくは80以上」
つまり、「20歳以下 or 80才以上の鈴木さんを探したい」場合です。
実際の例はこうなります。
$users = \App\User::where('name', 'like', '鈴木%') ->where(function($q){ $q->where('age', '<=', 20) ->orWhere('age', '>=', 80); }) ->get();
まず1つ目のwhere()は通常のあいまい検索で鈴木さんだけを抽出。そして、さらにwhere()をつなげてAND検索にするんですが、ここでfunction()を使って「20才以下 or 80才以上」というOR検索が実行しています。
重要なのは、function()を使ってAND検索の中にOR検索が入っているところです。つまり、以下のような通常通りのwhere()にしてしまうと、最後のorWhere()が有効になって鈴木さん以外のデータ(例えば、85才の佐藤さん)も入ってきてしまうのです。
(間違った例)
$users = \App\User::where('name', 'like', '鈴木%') ->where('age', '<=', 20) ->orWhere('age', '>=', 80) ->get();
特定の範囲を指定する
whereBetween()で範囲を指定する
例えば、20歳〜30歳までのユーザーだけ取得したい場合は、whereBetween()が使えます。
$users = \App\User::whereBetween('age', [20, 30])->get();
※この例では20才のデータも30歳のデータも含まれます。
whereNotBetweenで範囲「以外」を指定する
また、whereBetween()とは逆で指定の範囲以外のデータを取得することもできます。
$users = \App\User::whereNotBetween('age', [20, 25])->get();
※この例では20歳、30歳の人はデータに入っては来ません。
特定のカラムに複数の条件を指定する
whereIn()で該当するデータのみ取得する
例えば、複数の年齢があり、その年齢に該当する全てのデータを取得したい場合です。
$ages = [20, 30, 40]; $users = \App\User::whereIn('age', $ages)->get();
※この例では、20歳、30歳、40歳の人のデータだけ取得されます。
whereNotIn()で該当「しない」データのみを取得する
whereIn()と逆の検索方法です。以下の例は20歳、30歳、40歳「以外」のデータを取得します。
$ages = [20, 30, 40]; $users = \App\User::whereNotIn('age', $ages)->get();
Nullを検索条件にする
whereNull()でnullのデータだけ検索する
データがNullのものだけを取得するにはwhereNull()を使います。
$users = \App\User::whereNull('age')->get();
※SQL文だと「is NULL」とすべきところですが、こういうちょっとした例外もフレームワークが解決してくれるので便利ですね。
whereNotNull()でnull「以外」のデータだけ検索する
whereNull()とは逆にnull以外のものを取得する場合は以下のようになります。
$users = \App\User::whereNotNull('age')->get();
時間情報で検索する
whereDate()で日付を検索
例えば、「2000-01-01」という日付で検索する場合はwhereDate()を使います。
実際の例を見てみましょう。
$users = \App\User::whereDate('created_at', '2018-07-02')->get();
このwhereDate()がすごいのは、「日時データ」でも検索が可能なところです。
例えば、Laravelのテーブルにはデフォルトで「created_at」と「updated_at」というデータの「作成日時」と「更新日時」がわかるカラムがあります。ただ、このデータは以下のように「日付+時間」になっています。
通常なら最初の10文字を切り出して検索する必要がありますが、whereDate()を使えば、日付部分だけを自動的に検索してくれるというわけですね。
whereYear()で「年」を検索
「年」でデータ取得したい場合です。
例えば、2018年のデータだけを検索する場合は以下のようになります。
$users = \App\User::whereYear('created_at', '2018')->get();
※whereYear()もwhereDate()と同じく「日付+時間」データにも有効です。
whereMonth()で「月」を検索
同じく「月」で検索したい場合です。
例えば、7月のデータだけを取得する場合は以下のようになります。
$users = \App\User::whereMonth('created_at', '7')->get();
※月ごとの受注データなどを取得する場合に便利かもしれません。
※whereMonth()もwhereDate()と同じく「日付+時間」データにも有効です。
whereDay()で「日」を検索
同じく「日」で検索したい場合です。
例えば、3日のデータだけを取得する場合は以下のようになります。
$users = \App\User::whereDay('created_at', '3')->get();
※毎月○日のお客様サービスデーの情報だけ取得する、といった用途に使うと便利かもしれません。
※whereDay()もwhereDate()と同じく「日付+時間」データにも有効です。
whereTime()で「時間」を検索
同じく「時間」で検索したい場合です。
例えば、00時43分29秒のデータだけを取得する場合は以下のようになります。
$users = \App\User::whereTime('created_at', '00:43:29')->get();
※whereDay()もwhereDate()と同じく「日付+時間」データにも有効です。
※残念ながら時間、分、秒だけでの検索メソッドは用意されていないようです。
2つのカラムを比較して検索
例えば、登録日時(created_at)と更新日時(updated_at)が同じユーザー、つまり「登録後に何も更新していないユーザー」だけを検索したい場合にはwhereColumn()を使うといいでしょう。
実際の例はこうなります。
$users = \App\User::whereColumn('created_at', 'updated_at')->get();
なお、whereColumn()にはwhere()と同じく比較演算子が使えるので、以下のような使い方も可能です。
$users = \App\User::whereColumn('created_at', '<', 'updated_at')->get();
※この例は、「登録日時」よりも「更新日時」の方が後、つまり登録後に何か更新をしたユーザーだけを取得できます。
ちなみに、複数の条件を配列に入れて一気に検索することもできます。
$users = \App\User::whereColumn([ ['created_at', 'updated_at'], ['sent_at', '!=', 'updated_at'], ])->get();
これもwhere()と同じですね。
whereExists()で「ある条件」で存在するデータだけを取得する
例えば、「最低でも1回は問い合わせをしたユーザー」だけを検索したい場合はwhereExists()が便利です。
実際の例を見てみましょう。
$users = \App\User::whereExists(function($q){ $q->from('contacts') ->whereRaw('contacts.user_id = users.id'); })->get();
whereExists()の引数にはfunction()を使い、その中でqueryを作っています。
この方法を使えば、「1回は注文をしてくれたユーザー」なども抽出できるため、販売促進などに活用できるでしょう。
JSONデータの中身から検索
実は、Laravelのwhere()はjsonの中身を検索することもできます。例えば以下のようなデータです。
では、このデータの中から「os」が「mac」のものだけ取得してみましょう。
$users = \App\User::where('network->os', 'mac')->get();
重要なのは「->」の部分です。これでjson内のキーを指定することができるんですね。
もちろん、より階層が深くても検索も可能で、以下のように「->」でいくつもつなげることで実装できます。
$users = \App\User::where('network->browser->version', '67')->get();
※ただし、この機能はMySQLではバージョンが「5.7.8」以上でサポートされているので古い環境では使えない場合もあります。
リレーション先のテーブルから検索
whereHas()でリレーション先のテーブルから検索
例えば、「2018年7月1日以降に連絡してくれたユーザーだけを取得する」場合です。まず、これを実現するためには通常なら以下の手順が必要になってきます。
- 指定の日付以降のデータから該当するユーザーIDを取得
- ユーザーIDを使ってユーザーのデータを取得
しかし、Laravelではこれを一気に実現する方法が提供されています。それがwhereHas()です。
実際の例はこうなります。
$users = \App\User::whereHas('contacts', function($q){ $q->where('created_at', '>', '2018-07-01'); })->get();
まず第1引数の「contacts」はモデル内に追加されたリレーションシップで、今回は以下のようなhasMany()です。
public function contacts() { return $this->hasMany('App\Contact'); }
そして、このリレーション先の「Contact」テーブルで検索をするために第2引数のfunction()内でwhere()を指定しています。つまり、この部分で検索されたデータに「関連した」ユーザーだけを取得することができるのです。(=2018年7月1日以降に連絡をしてきたユーザー)
orWhereHas()でリレーション先のテーブルから検索(OR検索)
whereHas()のOR検索バージョンです。
詳しくは前項目のwhereHas()をご覧ください。
並び順を指定する
orderBy()で並び順を指定する
並び順を変更するにはorderBy()を使います。方法は簡単で、「カラム」と「昇順 or 降順」を指定するだけです。
$users = \App\User::orderBy('id', 'desc')->get();
ちなみに昇順と降順は以下のようになります。
- asc ・・・ 昇順。ascending orderの略。【例】1,2,3,4,5…
- desc ・・・ 降順。descending orderの略。【例】5,4,3,2,1…
日時を使った並び替え
latest()で最新のものから降順で並び替え
もしデータを作成した日時(created_at)で最新のものから降順で並び替えしたい場合はlatest()を使うとシンプルなコード記述ができます。
例えば、以下のようになります。
$users = \App\User::latest()->get();
こうするだけで、以下の同じ並べ替えをすることができます。
$users = \App\User::orderBy('created_at', 'desc')->get();
ちなみに、デフォルトは「created_at」ですが、以下のように好きなカラムに変更することもできます。
$users = \App\User::latest('updated_at')->get();
※ただし、データ数が多い場合、ソートは「id」を使った方がいいかもしれません。例えば、MySQLではプライマリーキーは自動でインデックスを作成するので、そちらのほうが高速に実行できる可能性があるからです。
oldest()で古いものから昇順で並び替え
latest()とは逆で、古いものから昇順でデータを並べ替えます。
$users = \App\User::oldest()->get();
なお、こちらもデフォルトのカラムは「created_at」ですが、好きなものを指定することができます。
$users = \App\User::oldest('updated_at')->get();
inRandomOrder()でランダムに並べ替え
あまり使うことは少ないかもしれませんが、ランダムで並べ替えをしたい場合のメソッドもあります。SQL文で言うところの「ORDER BY RAND()」ですね。
$users = \App\User::inRandomOrder()->get();
※このメソッドは、テストデータなどで使うといいかもしれません。
グループ化
groupBy()で特定のカラムをグループ化する
SQL文で言う、「GROUP BY *****」です。
例を見てみましょう。
$contacts = \App\Contact::select('user_id')->groupBy('user_id')->get();
ちなみに以下のようなエラーが発生する場合は、MySQL 5.7から「ONLY_FULL_GROUP_BY」というオプションがオンになっているために起こっているエラーで、意味は「グループ化したカラムしかselectはできないよ!」となります。
SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column
つまり、この場合は、以下のようにgroupByしている「user_id」もしくは、COUNT()などの集約関数しか使えないということになります。
$contacts = \App\Contact::select( 'user_id', \DB::raw('COUNT(id) AS IDS_COUNT') ) ->groupBy('user_id') ->get();
having()でグループ化した後のデータを絞り込む
where()と同じような使い方になりますが、having()はグループ化した後のデータで絞り込む(検索する)メソッドです。
実際の例を見てみましょう。
$contacts = \App\Contact::select( 'user_id', \DB::raw('COUNT(id) AS IDS_COUNT') ) ->groupBy('user_id') ->having('IDS_COUNT', '>', 3) ->get();
この例では、グループ化した件数をidで数え、さらにその件数が3件より多いものだけを取得するコードです。
skip()で取得する位置を指定する
データベースに多くのデータがある場合、「始めから10番目以降のデータを取得したい」という場合もでてきます。そんな場合はskip()を使いましょう。
$user = \App\User::skip(10)->first();
skip()の引数は、「どれだけデータを飛ばすか」の件数です。つまり、skip(1)とすると、idが2のデータが取得されるという意味になります。
※ちなみに、offset()も同じ機能をもつメソッドです。
take()でデータベースから取得する件数を指定する
SQL文に慣れ親しんだ人ならよく使ったことがある、「LIMIT文」になります。
$user = \App\User::take(5)->get(); // 5件だけ取得
※ちなみにlimit()も同じ意味になります。私もSQL分に慣れていたので始めはこちらの方が直感的でした。
when()でSQLクエリーを条件分岐させる
このメソッドはif文ようのような働きをするメソッドでtrue or falseの2つのクエリーを分岐させることができます。
実際に見てみましょう。
$and = true; $users = \App\User::when($and, function($q){ // AND検索 $q->where('id', request('id')) ->where('name', request('name')); }, function($q){ // OR検索 $q->where('id', request('id')) ->orWhere('name', request('name')); })->get();
この例は、「$and」がtrueのときはAND検索。そうでないならOR検索をするコードになります。
つまり、if文を使わなくても条件分岐ができるのでより可読性が高いコードを書くことができるわけですね。
集約したデータを取得する
count()で取得データの件数を取得する
取得したデータの件数を知りたい場合はcount()を使います。
$count = \App\User::count();
max()で最大値を取得する
max()を使うと指定したカラムの最大値を取得することができます。
例えば、「背が一番高い人のデータ」を取得する場合は以下のようになります。
$max_height = \App\User::max('height');
avg()で平均値を取得する
avg()を使うと平均値を取得することができます。「average」の省略形ですね。
例えば、「平均年齢」を知りたい場合は以下のようにします。
$avg_age = \App\User::avg('age');
データが存在しているかどうかをチェックする
exists()でデータの存在をチェック
exists()を使うと、データベース内にデータが存在しているかをチェックすることができます。
$exists = \App\User::where('id', 1)->exists(); if($exists) { echo '存在する!'; }
これは、idが1のデータが存在しているかどうかをチェックするコードで、取得できるデータは「true」か「false」です。
ここで重要なのは、データが1件でも存在していれば「true」になるところです。
例えば、次のコードです。今、データベースにはID「1」「2」「3」しか存在していないとします。whereIn()の中には実際に存在する「3」が指定されているのでこの場合は「4」と「5」は見つからないけれども、$existsは「true」となります。
$exists = \App\User::whereIn('id', [3,4,5])->exists();
doesntExist()でデータが存在「しないか」をチェック
exists()とは逆に存在「しないか」どうかをチェックするためのメソッドです。
$doesnt_exists = \App\User::where('id', 1)->doesntExist(); if($doesnt_exists) { echo '存在しない!'; }
このコードはデータが存在しない場合に「存在しない!」と表示されることになります。
文字では少し説明が難しいですが、doesntExist()は「存在しない」場合「true」を返し、「存在する」場合は「false」を返します。
ただ、この辺は少し理解がややこしいので、できるだけ直感的につかえるexists()を使うことをおすすめします。
SQL文をそのまま指定する
\DB::raw()でSQL文を直書きする
LaravelではDBクエリーのためのメソッドが事細かく提供されているので、ほぼどんな場合でもそれらで対応できるようになっています。
ただ、やはり例外というものは存在していて、まれに直接SQL文を書きたい場合もでてくると思います。そんな場合には\DB::raw()を使ってSQL文を直書きしましょう。
例えば以下のような、SQLの中に「COUNT()」などの集約関数が必要な場合などです。
$users = \App\User::select(\DB::raw('COUNT(id) as id_count'))->get();
ちなみに「raw」とは「生(なま)の」という意味です。「raw fish」は日本語で「生魚」。つまり、SQLの「生の文章」ということですね。
selectRaw()でselectでSQL文を使う
selectRaw()を使うと、\DB::raw()を使わずより可読性が高いコードを書くことができます。
$users = \App\User::selectRaw('COUNT(id) as id_count')->get();
whereRaw()でwhere句にSQL文を使う
whereRaw()を使えば、where句にSQL文を直書きすることができます。
例えば、名前が(シングルバイトで)10文字以上のユーザーを取得する場合です。
$users = \App\User::whereRaw('LENGTH(name) > 10')->get();
orWhereRaw()でwhere句にSQL文を使う(OR検索)
whereRaw()のOR検索バージョンです。詳しくは上の項目をご覧ください。
$users = \App\User::where('id', 1) ->orWhereRaw('LENGTH(name) > 10') ->get();
havingRaw()でhaving句にSQL文を使う
having句はグループ化した後のデータを絞り込むもので、これにSQL文を直書きしたい場合にhavingRaw()を使います。
例えば、「連絡をしてきた件数が5件より多いユーザーID」を取得する場合は以下のようになります。
$contacts = \App\Contact::select('user_id') ->groupBy('user_id') ->havingRaw('COUNT(user_id) > 5') ->get();
orHavingRaw()でhaving句にSQL文を使う(OR検索)
havingRaw()のOR検索バージョンです。詳しくは上の項目をご覧ください。
$contacts = \App\Contact::select('user_id') ->groupBy('user_id') ->havingRaw('COUNT(user_id) < 3') ->orHavingRaw('COUNT(user_id) > 10') ->get();
orderByRaw()でORDER句にSQL文を使う
orderByRaw()を使うとORDER句に直接SQL文を使うことができます。
$users = \App\User::orderByRaw('RAND()')->get();
テーブルを結合させる
※せっかくLaravelにはリレーションシップ機能が備わっているので、特別な理由がない場合は、EloquentモデルのhasOneやhasManyなどを使うことをおすすめします。
join()で内部結合する
join()を使うとSQL文で言うところの「INNER JOIN」を実装することができます。
$user_contacts = \DB::table('users') ->select('contacts.id', 'users.name') ->join('contacts', 'users.id', '=', 'contacts.user_id') ->get();
引数の意味は以下のとおりです。
- 第1引数 ・・・ 結合したいテーブルの名前
- 第2、4引数 ・・・ 結合したいカラム
- 第3引数 ・・・ 比較演算子(基本的には「=」でOKです)
leftJoin()で外部結合する
SQL文の「LEFT JOIN」を実装するメソッドです。引数の内容はjoin()と同じなので、上の項目をご覧ください。
$user_contacts = \DB::table('users') ->select('contacts.id', 'users.name') ->leftJoin('contacts', 'users.id', '=', 'contacts.user_id') ->get();
crossJoin()でクロス結合する
SQL分の「CROSS JOIN」を実行するメソッドです。
$user_contacts = \DB::table('users') ->select('contacts.id', 'users.name') ->crossJoin('contacts') ->get();
複雑な結合
あまり利用する頻度はないかもしれませんが、複雑な結合をするには以下のようにjoin()内にfunction()を指定することで実装が可能です。
$user_contacts = \DB::table('users') ->join('contacts', function ($join) { $join->on('users.id', '=', 'contacts.user_id') ->orOn('users.created_at', '=', 'contacts.created_at') ->where('contacts.created_at', '> ', '2018-01-01 00:00:00'); })->get();
function()内には、on(), orOn(), where(), orWhereなどが使えます。
union()で2つのテーブルデータを1つにする
2つのテーブルデータを一つにまとめたい場合はunion()が使えます。
$query_1 = \App\User::where('id', '<', 3); $query_2 = \App\User::where('id', '>', 5); $users = $query_2->union($query_1)->get();
ここで重要なのは、union()の中に入れるのは「クエリー」だということです。つまり、get()やfirst()は不要です。
ページごとにデータを取得する(Pagination)
paginate()でページごとのデータを取得
データベースの操作で「簡単そうで実はロジックが難しい開発」のひとつに「Pagination(ページ管理)」があります。
Laravelではこのページ管理も直感的に使えるような機能が備わっているので以下に紹介したいと思います。
では、実例です。
$per_page = 5; $users = \App\User::paginate($per_page);
この例では、5件ずつユーザー・データを取得しています。
なお、paginate()を使って取得したデータはget()で取得したものと同じく以下のようにforeach()で取得することができます。
foreach ($users as $user) { echo $user->name; }
ただ、get()と違うのは通常のデータだけではなく、様々なページ情報を取得できるところです。
それぞれ実例コードを見ていきましょう。
count()で表示しているページのデータ件数を取得する
現在表示しているページのデータ件数です。もし、「全部で何件取得したか」をチェックしたい場合はtotal()を使いましょう。
echo $users->count();
currentPage()で現在のページ番号を取得する
echo $users->currentPage();
firstItem()で最初のデータ番号を取得する
表示ページのデータの中から一番最初のデータ番号を取得する。つまり、paginate(3);としておいて、現在1ページ目にいるなら「1」、2ページ目にいるなら「4」となります。ページfrom〜toの「from」。
echo $users->firstItem();
hasMorePages()でまだ表示すべきデータが残っているかチェックする
例えば、現在2ページ目を表示していて、次の3ページ目が存在しているかどうかをチェックする場合です。
if($users->hasMorePages()) { echo 'まだ表示するページが残っています!'; }
lastItem()で最後のデータ番号を取得する
表示ページのデータの中から一番最後のデータ番号を取得する。つまり、paginate(3);としておいて、現在1ページ目にいるなら「3」、2ページ目にいるなら「6」となります。ページfrom〜toの「to」。
echo $users->lastItem();
lastPage()で最後のページ番号を取得する
つまり、この数字はどのページでも同じものになります
echo $users->lastPage();
nextPageUrl()で次ページのURLを取得する
次ページのURLです。もし次のページがない場合はnullになります。
echo $users->nextPageUrl();
perPage()ページごとに何件なのかを取得する
つまり、paginate()で指定した数字です。
echo $users->perPage();
previousPageUrl()で前ページのURLを取得する
前ページのURLです。もし次のページがない場合はnullになります。
echo $users->previousPageUrl();
total()で全データ件数を取得する
全データ件数を取得できます。表示ページの件数を取得したい場合はcount()を使いましょう。
echo $users->total();
url()で特定ページ番号のURLを取得する
例えば、3ページ目のURLを取得する場合は以下のようになります。
echo $users->url(3);
links()でページリンクを作成する
paginate()を使って取得したデータはテンプレート内で以下のようにlinks()を使うと、自動的にulタグとliタグのページリンクを表示してくれます。
ちなみにCSSフレームワークで有名なbootstrapを読み込むと自動的に以下のような装飾になります。
実際のコードは以下になります。
(コントローラー側)
class HomeController extends Controller { public function index() { $per_page = 3; $users = \App\User::paginate($per_page); return view('index')->with([ 'users' => $users ]); }
(Blade側)
<html> <head> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div> {{ $users->links() }} </div> </body> </html>
withPath()でページリンクのURLを変更する
リンクのURLを変更したい場合はwithPath()を使ってください。
$users->withPath('users/detail');
この場合、「https://example.com/users/detail?page=(ページ番号)」がURLになります。
appends()で独自のパラメータをURLに追加する
ページリンクのURLに独自のパラメータを追加したい場合は以下のようにappends()を使います。
$users->appends([ 'key_1' => 'value_1', 'key_2' => 'value_2', 'key_3' => 'value_3' ]);
これで、ページのURLは「https://example.com/*****?key_1=value_1&key_2=value_2&key_3=value_3&page=(ページ番号)」となります。
fragment()で「#」付きのURLにする
$users->fragment('bottom');
この場合のURLは、「http://example.com/***#bottom」となります。自動的にページ内移動したい場合や、JavaScriptなどでstateを管理している場合に重宝します。
simplePaginate()で簡易的にページごとのデータを取得する
simplePaginate()は、ほぼpaginate()と同じですが違うのはページ表示が「前」「次」だけになることです。
実際の表示はこうなります。
ただし、上のように日本語を表示するためには「/resouces/lang/ja/pagination.php」にファイルを作り、以下のようなデータを追加する必要があります。
<?php return [ 'previous' => '« 前', 'next' => '次 »', ];
また、simplePaginate()の特徴から、以下のメソッドは使えなくなっています。
- lastPage()
- total()
ソフトデリート(論理削除)されたデータを取得する
withTrashed()でソフトデリートされたデータも含めてデータ取得する
withTrashed()を使えば、すでにソフトデリートされたデータも含めて取得することができます。管理者向けコンテンツを開発するのに重宝するかもしれません。
$items = \App\Item::withTrashed()->get();
onlyTrashed()でソフトデリートされたデータだけを取得する
withTrashed()とは違って、ソフトデリートされてデータ「だけ」を取得する方法です。「ゴミ箱」コンテンツなどをに使えます。
$items = \App\Item::onlyTrashed()->get();
データが見つからない場合は考慮したデータ取得
もしあるデータが見つからない場合はエラーを発生させたい場合のやり方です。ひとつずつ見ていきましょう。
firstOrFail()でデータがない場合はエラー(例外)を発生させる
例えば、現在IDが「5」のユーザーは存在してないとします。
\App\User::where('id', 5)->firstOrFail();
すると、上記を実行した場合、以下のように404エラーとなります。
もし、404レスポンスを発生させたくない場合はtry〜catchで例外として処理ができます。
try { $user = \App\User::where('id', 5)->firstOrFail(); } catch (\Exception $e) { echo $e->getMessage(); }
findOrFail()でデータが見つからない場合はエラー(例外)を発生させる
これも前項目のfirstOrFail()とほぼ同じですが、引数はテーブルのIDを指定します。
$id = 5; \App\User::findOrFail($id);
おわりに
ということで今回はデータベースからデータを取得する方法をまとめてみました。・・・が、結果として20,000文字を軽く越える情報量になってしまいました。それだけLaravelの守備範囲が広いということですね。
これからも痒いところに手が届くフレームワークであってほしいです。
(なお、全実例とタイトルに書いてますが、もし抜けているものがあったらぜひご連絡ください。お願いいたします。m_ _m)
ではでは!