ページめくり

おまとめ三行

基本形
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を触ってみましたの記事はこちら
まとめという名の箸休め