CakePHPを利用する場合には、クエリービルダーを利用する機会が多いと思います。
SQLを直接記述して実行することも可能ですが、可能な限り、クエリービルダーを利用する方が、メリットが多いです。
また、クエリーを組み立てることで、 union やサブクエリーを使った 高度なクエリーも簡単に作成しデータを取得することが可能です。
セキュリティー面でも、クエリービルダーの裏側では PDO プリペアードステートメント(prepared statement)を使うことで、 SQL インジェクション攻撃から守られるなど、高度の保護されています。
本記事では、CakePHP よく利用するクエリービルダーの詳細について解説しています。
全件を取得する
無条件に全件を取得するには、下記のように find を利用します。
1 2 3 4 5 6 7 |
use Cake\ORM\TableRegistry; $query = TableRegistry::getTableLocator()->get('Users')->find(); foreach ($query as $user) { debug($user); } |
また、all()、toList()を利用することで、クエリーを直接実行することができます。
1 2 3 4 5 6 7 |
$query = $users ->find() ->all(); $query = $users ->find() ->toList(); |
条件を指定する
条件項目が1つの場合には、下記のようになります。
1 2 3 4 5 6 7 8 9 10 11 |
$query = $users ->find() ->where([ 'Authority' => 99, 'OR' => [ ['status' => 1], ['status' => 2] ], ]) ->order(['created' => 'DESC']); foreach ($query as $user) { debug($user); } |
複数の検索条件を指定するには、下記のようになります。
1 2 3 4 5 6 7 8 9 10 |
$query = $users ->find() ->where([ 'id !=' => 1 ]) ->order(['created' => 'DESC']); foreach ($query as $user) { debug($user); } |
IN句を指定する
1 2 3 4 5 6 7 8 9 10 |
$query = $users ->find() ->where([ 'id IN' => [1, 2, 3, 4, 5] ]) ->order(['created' => 'DESC']); foreach ($query as $user) { debug($user); } |
IS NULLを指定する
1 2 3 4 5 6 7 8 9 10 |
$query = $users ->find() ->where([ 'kana IS' => null ]) ->order(['created' => 'DESC']); foreach ($query as $user) { debug($user); } |
または、下記の指定でも取得可能です。
1 2 3 4 5 6 7 8 9 10 |
$query = $users ->find() ->where([ 'kana IS NULL' ]) ->order(['created' => 'DESC']); foreach ($query as $user) { debug($user); } |
先頭行を取得する
1 2 3 4 5 6 |
$query = $users ->find() ->order(['created' => 'DESC']) ->first(); debug($user); |
カラムから値リストを取得する
カラムから値リストを取得するには、Collection ライブラリーの extract() メソッドを利用します。
1 2 3 4 5 6 |
$query = $users ->find() ->extract('name') ->all(); debug($user); |
クエリーの結果から、key-value リストを生成することも可能です。
1 2 3 4 5 |
$query = $users ->find('list') ->all(); debug($user); |
combine を利用して、リストを生成することも可能です。
1 2 3 4 5 6 |
$query = $users ->find() ->combine('id', 'name') ->all(); debug($user); |
select項目を変更する
1 2 3 4 5 6 7 8 |
$query = $users ->find() ->select(['id', 'name']) ->order(['created' => 'DESC']); foreach ($query as $user) { debug($user); } |
連想配列を利用することで、フィールドのエイリアス(別名)をセットすることが可能です。
1 2 3 4 5 6 7 8 9 10 11 12 |
$query = $users ->find() ->select([ 'pk' => 'id', 'user_name' => 'name', 'kana' ]) ->order(['created' => 'DESC']); foreach ($query as $user) { debug($user); } |
select distinct するためには、distinct() メソッドを利用することができます。
1 2 3 4 5 6 7 8 9 |
$query = $users ->find() ->select(['id', 'name']) ->order(['created' => 'DESC']) ->distinct(['id']); foreach ($query as $user) { debug($user); } |
select($fields) を呼んだ後に、テーブルのすべてのフィールドを選択するには、 下記の方法でテーブルインスタンスを select() に渡すことができます。
この例では、first_nameとlast_nameを連結したfull_name と usersテーブルのすべての項目を取得しています。
1 2 3 4 |
$query = $users->find(); $query ->select(['full_name' => $query->func()->concat(['first_name' => 'identifier', '-', 'last_name' => 'identifier'])]) ->select($users); // users のすべてのフィールドを選択する |
条件に無名関数を指定する
1 2 3 4 |
$query = $articles->find(); $query->where(function (QueryExpression $exp, Query $q) { return $exp->eq('status', 1); }); |
ソート条件を指定する
1 2 3 4 5 6 7 8 9 10 11 |
$query = $users ->find() ->order([ 'name' => 'ASC', 'created' => 'DESC', ]) ->all(); foreach ($query as $user) { debug($user); } |
複合的な式のソート条件を指定する
複数の項目を連結した複合的な式でソートをするには、order 以外に、 orderAscとorderDescメソッドを利用することができます。
1 2 3 4 5 6 |
$query = $users->find(); $concat = $query->func()->concat([ 'first_name' => 'identifier', 'last_name' => 'identifier' ]); $query->orderAsc($concat); |
SQL関数を利用する
1 2 |
$query = $users->find(); $query->selec(['count' => $query->func()->coun('*')]); |
SQL関数には、多くのおなじみの関数が func() メソッドとともに作成することができます。
SQL関数一覧
関数 | 説明 |
---|---|
rand | 0から1の間の乱数をSQLで生成します。 |
sum | 合計を算出します。 引数はリテラル値として扱われます。 |
avg | 平均値を算出します。 引数はリテラル値として扱われます。 |
min | カラムの最小値を算出します。 引数はリテラル値として扱われます。 |
max | カラムの最大値を算出します。 引数はリテラル値として扱われます。 |
count | 件数を算出します。 引数はリテラル値として扱われます。 |
concat | 2つの値を結合します。 引数はバインドパラメーターとして扱われます。 |
coalesc | Coalesc を算出します。 引数はバインドパラメーターとして扱われます。 |
dateDiff | 2つの日にち/時間の差を取得します。 引数はバインドパラメーターとして扱われます。 |
now | デフォルトでは日付と時刻を返しますが、 'time' または 'date' を指定してこれらの値のみを返すこともできます。 |
extract | SQL 式から特定の日付部分(年など)を返します。 |
dateAdd | 日付式に単位時間を加算します。 |
dayOfWeek | SQL の WEEKDAY 関数を呼ぶ FunctionExpression を返します。 |
集約 - Grouping と Having
count や sum などの集約関数を利用するするには、 下記の集約メソッドを利用することができます。
以下は、登録日が同一のユーザー数が10人以上のデータを取得しています。
1 2 3 4 5 6 7 |
$query = $users->find(); $query->select([ 'count' => $query->func()->count('view_count'), 'registed_date' => 'DATE(created)' ]) ->group('registed_date') ->having(['count >' => 10]); |
Case文
成績表ごとの得点を確認したいクエリーは、下記のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$query = $grades->find() ->where(function (QueryExpression $exp, Query $q) { return $exp->addCase( [ $q->newExpr()->lt('point', 100), $q->newExpr()->between('point', 101, 999), $q->newExpr()->gte('point', 1000), ], ['SMALL', 'MEDIUM', 'LARGE'], # 条件に合致したときの値 ['string', 'string', 'string'] # それぞれの値の型 ); }); # WHERE CASE # WHEN point < 100 THEN 'SMALL' # WHEN point BETWEEN 100 AND 999 THEN 'MEDIUM' # WHEN point >= 1000 THEN 'LARGE' # END |
レコードの合計数を取得する
Query オブジェクトを使って、条件の結果見つかった行の合計数を取得することができます。
1 2 3 4 5 6 |
$query = $users ->find() ->where(['status' => 1]) ->count(); debug($user); |
関連付くデータを取得する
Query オブジェクトを使って、条件の結果見つかった行の合計数を取得することができます。
1 2 3 4 5 6 7 |
$query = $users ->find() ->contain(['Comments']) ->where(['status' => 1]) ->all(); debug($user); |
また、下記のように、深い関連データをイーガーロードすることができます。
1 2 3 4 5 6 7 8 9 10 11 |
$query = $users ->find() ->contain([ 'Comments', 'Comments.Kinds', 'Comments.Histories', ]) ->where(['status' => 1]) ->all(); debug($user); |
関連を含んだソートの指定
1 2 3 4 5 |
$query->contain([ 'Comments' => [ 'sort' => ['Comments.created' => 'DESC'] ] ]); |
joinを追加する
複数 join の連想配列を渡すことで、複数の join を一度に追加できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$query = $users->find() ->join([ 'c' => [ 'table' => 'comments', 'type' => 'LEFT', 'conditions' => 'c.user_id = users.id', ], 'h' => [ 'table' => 'histories', 'type' => 'LEFT', 'conditions' => 'h.comment_id = comments.id', ] ]); |
クエリー補足
クエリーの遅延評価
CakePHPのクエリーの実行タイミングを理解しておくことは非常に重要です。
Cookbookには、下記の説明があり、一読されることをおススメいたします。
Query オブジェクトは遅延評価されます。 これはクエリーが次のいずれかが起こるまで実行されないということを意味します。
クエリーが foreach() でイテレートされる。
クエリーの execute() メソッドが呼ばれる。これは下層の statement オブジェクトを返し、 insert/update/delete クエリーで使うことができます。
クエリーの first() メソッドが呼ばれる。 SELECT (それがクエリーに LIMIT 1 を加えます) で構築された結果セットの最初の結果が返ります。
クエリーの all() メソッドが呼ばれる。結果セットが返り、 SELECT ステートメントでのみ使うことができます。
クエリーの toList() や toArray() メソッドが呼ばれる。
このような条件が合致するまでは、 SQL をデータベースへ送らずに、クエリーを変更することができます。 つまり、 Query が評価されないかぎり、SQL はデータベースへ送信されないのです。 クエリーが実行された後に、クエリーを変更・再評価したら、追加で SQL が走ることになります。Cookbook:クエリーの遅延評価
Cookbook
クエリーオブジェクト
Cookbook:https://book.cakephp.org/4/ja/orm/query-builder.html#id2
まとめ
いかがだったでしょうか?
CakePHPのクエリービルダーには、非常に便利な機能がたくさんあります。
利用する機会が少ないものもありますが、
知っておくだけでも、コードがスッキリする場合があります。
詳細まで把握しておく必要はないと思いますが、
こんな機能があるのだという程度でもよいので、
あたまのスミに置いておくと良いと思います。
また、CakePHPの公式サイトの Cookbookは非常に良くまとまっているサイトです。
ぜひ、熟読されることをおススメいたします。