CakePHP3を触ってみました 〜ページをめくってもスカートはめくるな〜

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
ページめくり

この記事を三行にまとめると

基本形
findメソッドと組み合わせる
finderを使う
今日はCakePHP3でページネーションをやってみたいと思います。いくつか書き方があるので順番に見ていきましょう。

今回はpostsというテーブルにブログ記事が入っていて、ページネーションを使ってその一覧を取得するという想定でやってみます。



基本形

まずは一番基本的な形。例えば今年の一月一日以降に投稿された記事の一覧を、投稿日の新しい順に取ってきたいとする。

//PostsController.php
class PostsController extends AppController {
  public $paginate = [];

  public index() {
    $this->paginate = [
      'conditions' => ['post_date >=' => '2017-01-01'],
      'fields' => ['id', 'title', 'text', 'post_date'],
      'order' => ['post_date' => 'DESC']
    ];
    $data = $this->paginate('Posts');
  }
}

これでオッケーです。「post_date」というフィールドに投稿日が入ってると思ってください。

CakePHP2系の時とほとんど同じ書き方ですね。CakePHP3ではfindのとき、「conditions」や「fields」の部分が「where」や「select」に変わりましたが、paginateを使用する場合は今まで通りにconditionsとfieldsを使います。むしろこの書き方だとwhereやselectは効かない。エラーにはならないようですがSQL文の作成には使われないっぽいです。

//PostsController.php
class PostsController extends AppController {
  public $paginate = [];

  public index() {
    //order以外は反映されない
    $this->paginate = [
      'where' => ['post_date >=' => '2017-01-01'],
      'select' => ['id', 'title', 'text', 'post_date'],
      'order' => ['post_date' => 'DESC']
    ];
    $data = $this->paginate('Posts');
  }
}



findメソッドと組み合わせる

じゃあwhereやselectを使って書くことはできないのかってーとそんなことはない。3系では通常のfindメソッドを引数に渡してページングつきのデータを取ってくることもできます。2でも似たようなことはできますがね。

//PostsController.php
class PostsController extends AppController {
  public $paginate = [];

  public index() {
    //findメソッドでクエリを作成
    $query = $this->Posts->find();
    $query->where(['post_date >=' => '2017-01-01']);
    $query->select(['id', 'title', 'text', 'post_date']);
    $query->order(['post_date' => 'DESC']);

    //$queryを渡してデータを取得
    $data = $this->paginate($query);
  }
}

こんな感じです。findメソッドを使えるってのは便利かもしれません。クエリ作成部分をテーブル側で作成すればコントローラーがすっきりするしね。

//PostsController.php
class PostsController extends AppController {
  public $paginate = [];

  public index() {
    $query = $this->Posts->getQuery();
    $data = $this->paginate($query);
  }
}

//PostsTable.php
class PostsTable extends Table {
  public function getQuery() {
    $query = $this->find();
    $query->where(['post_date >=' => '2017-01-01']);
    $query->select(['id', 'title', 'text', 'post_date']);
    $query->order(['post_date' => 'DESC']);
    return $query;
  }
}



finderを使う

findメソッドとの組み合わせに少し似ているのですが、テーブルにデータの取得条件を書いたfinderを作成しておくというやり方もあります。

finderってのはえーと……実際にコードを書いた方が早いかな。ページネーションじゃなくて通常のfindの時にfinderを使うと以下のようになります。

//PostsController.php
class PostsController extends AppController {
  public index() {
    $data = $this->Posts->find('post');
  }
}

//PostsTable.php
class PostsTable extends Table {
  public function findPost(Query $query) {
    $query->where(['post_date >=' => '2017-01-01']);
    $query->select(['id', 'title', 'text', 'post_date']);
    $query->order(['post_date' => 'DESC']);
    return $query;
  }
}

テーブルに「find〇〇」というメソッドを作っておくと、findの引数に「〇〇」の部分を渡すだけで「find〇〇」の中に書いた取得条件が適用されます。「find〇〇」の方の先頭は大文字で、findの引数の方は小文字です。上記の例だと「post」と書いてますが、自分はスカートが大好きだからメソッド名にもスカートを使いたいって場合はこうなる。

//PostsController.php
$this->Posts->find('skirt');

//PostsTable.php
public function findSkirt(Query $query) {}

同じことがページネーションでもできます。ちょびっとだけ書き方が違うけど。

//PostsController.php
class PostsController extends AppController {
  public $paginate = [];

  public index() {
    $this->paginate = ['finder' => 'skirt'];
    $data = $this->paginate('Posts');
  }
}

//PostsTable.php
class PostsTable extends Table {
  public function findSkirt(Query $query) {
    $query->where(['post_date >=' => '2017-01-01']);
    $query->select(['id', 'title', 'text', 'post_date']);
    $query->order(['post_date' => 'DESC']);
    return $query;
  }
}

「[‘finder’ => ‘〇〇’]」と書くことで先ほどと同じように「find〇〇」が使えます。



finderに値を渡す

「find〇〇」メソッドにコントローラーから値を渡したい場合。

//PostsController.php
class PostsController extends AppController {
  public $paginate = [];

  public index() {
    $this->paginate = [
      'finder' => [
        'skirt' => ['date' => '2017-01-01']
      ]
    ];
    $data = $this->paginate('Posts');
  }
}

//PostsTable.php
class PostsTable extends Table {
  public function findPost(Query $query, $options) {
    $query->where(['post_date >=' => $options['date']]);
    $query->select(['id', 'title', 'text', 'post_date']);
    $query->order(['post_date' => 'DESC']);
    return $query;
  }
}

finderを指定する際、配列の階層を一つ深くすることでfindPostメソッドの第二引数(上の例だと$options)に値が渡ります。



URLのパラメータ

CakePHP2の時は「page:2」みたいな書き方をしてましたが、3では通常のGETパラメータと同じ感じです。

//CakePHP2
/posts/index/page:2

//CakePHP3
/posts/index?page=2






こんな感じですかねー。

さっきもちょろっと言いましたが、個人的にfindメソッドを組み合わせたりfinderが使えるのは便利だと思います。CakePHP2を使ってた頃、普通のfindに関する処理は基本的にモデルに書いてたのに、ページネーションの時だけコントローラーに処理を書くってのがちょっと気に入らなかったんですよね。どうせならページネーションもモデルに書いてコントローラーで呼び出せればコードがもっとすっきりするのにって思ってた。

あ、テンプレート側の書き方は2と同じで大丈夫そうです。なのでここでは省略。



その他のCakePHP3を触ってみましたの記事はこちら
まとめという名の箸休め
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください