PHP
CakePHP
cakephp3

CakePHP3の使い方まとめ


この投稿について

そろそろ正式版がリリースされそうなCakePHP3のβ版を触ってみたので、その基本的な使い方をまとめます。

各機能ともあまり深入りせず、主要な機能だけまとめています。

Cake2系より、Model周りが大きく変わっています。

スクリーンショット 2014-12-05 19.02.12.png


Installation


  • PHP5.4以上が必要

  • インストールはcomposerで行う

まずは作業ディレクトリで、composerをインストール

> mkdir cake3

> cd cake3
> curl -s https://getcomposer.org/installer | php

次に以下のコマンドで、CakePHP3のアプリがセットされる。

php composer.phar create-project --prefer-dist -s dev cakephp/app [app_name]

これだけ。簡単!


Configuration

Configurationはconfig/bootstrap.phpから読み込まれ、初期はconfig/app.phpというConfigファイルが1つだけある。

config/以下に任意のConfigファイルを追加してconfig/bootstrap.phpに以下のように追記すれば、設定を上書きできる。


config/bootstrap.php

Configure::config('default', new PhpConfig());

Configure::load('app', 'default', false);
Configure::load('chocolate', 'default'); # config/chocolate.php という設定ファイルを追加し、それを読み込む

Configファイルで設定できるものは、DB、キャッシュ、デバッグレベル、基底パス等等


Routing


Routing設定

config/routes.phpで設定する。

次のようにすると、/bookでアクセスされた場合に、BooksControllerのindexアクションにRoutingされる。


config/routes.php

Router::connect('/book', ['controller' => 'Books', 'action' => 'index']);


次のようにすると、/book/15や、/book/fooでアクセスされた場合に、BooksControllerのviewアクションにRoutingされる。

この:somethingのようにすればプレースホルダーとなって、Controller側でsomethingをキーにして値が取得できる。


config/routes.php

Router::connect('/book/:book_id', ['controller' => 'Books', 'action' => 'view']);


次のように第3引数にパラメータ(:book_id)が取りうる値を正規表現で指定することも可能。この場合、/book/22はマッチするが、/book/phpはマッチしないのでこのRoutingの指定が適用されない。


config/routes.php

Router::connect('/book/:book_id', ['controller' => 'Books', 'action' => 'view'], ['book_id' => '\d+']);



Reverse Routing

ここまでは、特定のURLを特定の処理(Controllerとアクション)にRouting(マッピング)してきたが、反対に特定の処理(Controllerとアクション)からマッチするURLを出力することも可能であり、次のようになる。

echo Router::url(array('controller'=>'Books', 'action'=>'index')); # /book を出力

echo Router::url(array('controller'=>'Books', 'action'=>'view', 'book_id'=>14)); # /book/14 を出力

このような逆向きの変換をReverse Routingという。


RequestとResponse


Request



  • Requestはデフォルトのリクエストを扱うクラスで、リクエストのデータの管理・操作の責任をもつ(ソースはCake\Network\Request


  • $this->requestで、Controller、View、Cell、Helperからアクセス可能。

リクエストパラメータにアクセスするには、以下のように複数の方法がある

$this->request->params['controller'];

$this->request->param('controller'); # param[s]ではなくparamなのに注意!


クエリパラメータ

/book?hoge=2&fuga=3のようなURLでアクセスされた場合、次のようにするとクエリ部分のパラメータを取得可能

$this->request->query; # array('hoge' => '2','fuga' => '3')


POSTデータ

POSTされたデータは次のようにして取得可能

$this->request->data('Hoge.fuga');

$this->request->data['hoge'];


Response


  • HTTPレスポンスを操作する責任もつ

  • Content-Type、文字コード、Headerなどを操作

$this->response->type('text/html');

$this->response->charset('UTF-8');
$this->response->header('Location', 'http://google.com');


Controller


  • MVCのC

  • Controllerはできるだけ薄く(コード量を少なく)して、Modelに処理を書くのが鉄則。

  • この鉄則に従えば、コードの再利用性が高まり、テストも書きやすくなる。

  • 一般的に1つのControllerは1つのModelに対応するが、複数のモデルを扱うことも当然できる。そのような場合はControllerで扱うModelの中で最も主要なModelに対応した名前をつける。

  • 各ControllerはAppController(src/Controllers/AppController.php)を継承する。

  • Controllerには複数のリクエストを扱うメソッドがあり、これらをアクションという。アクションは対応するURLからアクセスすることができ、リクエストを操作しレスポンスを返すことが責任である。

Controllerの実装は次のようになる。


src/Controllers/BooksController.php

<?php 

namespace App\Controller;

class BooksController extends AppController {

public function index()
{
//something...
}

public function view()
{
//something...
}

}



AppController

各ControllerはAppControllerを継承しているので、多くのControllerから同じ処理を行う場合はAppControllerに処理を書くのがよい。


Viewのレンダリングと値のセット

Cakeの規約によりデフォルトではアクション名に応じたViewがレンダリングされる。

規約外のViewをレンダリングしたい場合は$this->renderを使って、明示的にレンダリングしたいテンプレート名を指定。


src/Controllers/BooksController.php

<?php 

namespace App\Controller;

class BooksController extends AppController {

public function view()
{
// src/Template/Books/view.ctpをレンダリング
}

public function add()
{
// src/Template/Books/add.ctpをレンダリング
}

public function index()
{
$this->render('special')
       // src/Template/Books/index.ctpの代わりに、src/Template/Books/special.ctpをレンダリング
}

}


ContrllerからViewにデータを渡したい場合は次のようにする

// at Controller

$this->set('name', 's-kiriki');

// at View Template
私の名前は<?= h($name); ?>です。# 私の名前は、s-kirikiです


View


  • MVCのV

  • HTML、JSON、PDFなどの出力処理を担当


Viewテンプレート



  • src/Template/以下に配置。


  • AnimalsContllereditアクションであれば、規約によりsrc/Template/Animals/edit.ctpがレンダリングされる。

Viewレイヤーは以下のようないくつかの要素から構成される。

要素
説明

views
アクションごとに独自なページのパーツであり、レスポンスの主要な部分を形づくる

elements
小規模かつ再利用可能なパーツ切り出したもので、通常はviewsから呼ばれる

layouts
レイアウトファイル(viewsをwrap)

helpers
viewレイヤーでのロジックを再利用可能にカプセル化するためのもの。例)フォーム作成

cells
UIコンポーネントを作成するための小規模なControllerのような機能を提供するもの


View変数

Contollerでset()を使ってセットしたものをviewやelementで利用可能。Cakeは勝手にエスケープしてくれないのでh()関数でエスケープする必要あり。

# at view or element

<?= h($hoge); ?>


Elements

viewからは次のように呼び出せる。

<?php echo $this->element('my-element'); ?>

elementに変数を渡したい場合は次のようにする。

<?php echo $this->element('menu', ['price'=>400]); ?>

# または
<?php echo $this->element('menu', array('price'=>400)); ?>
# これでelements内では$price=400となる。


Viewブロック

以下のような感じで、パーツを組み合わせて自由なまとまりを作ることができる。

<?php 

// food-listという名前でViewBlockを作成
$this->start('food-list'); // 設定開始
echo $this->element('food/ramen'); // ラーメンのelementを追加
echo $this->element('food/pasta'); // パスタのelementを追加
$this->end(); // 設定終了

// その後また追加したい場合
$this->start('food-list');
echo $this->fetch('food-list'); // 既存のfood-listを持ってくる
echo $this->element('food/fruits'); // パスタのelementを追加
$this->end();
?>

<?= $this->fetch('food-list') ?> # このfetch()で最終的に出力


Model


  • MVCのM

  • ビジネスロジックを担当するレイヤー

  • ORMはこれまでの連想配列ではなくオブジェクトを返すようになった(Cake1、2系に比べて大きく変わった)

Modelには大きく分けて、次のような2種類のオブジェクトがある。


  1. Tableオブジェクト

    データのコレクションにアクセスを提供。

    データのCRUD(作成/更新/削除)や、関連の作成、bulk操作(一度に複数のデータを操作すること)を担当


  2. Entity

    各レコードを表現し、振る舞いや機能を定義する


ActiveRecordDatamapperパターンの両方のアイデアに影響をうけて作られており、

1.素早く作れる

2.ORMを簡単に操作できる

という両方の利点を実現している。


Tableオブジェクト

特定のテーブルのデータコレクションにアクセスする。

ただし、TableオブジェクトおよびORMを使うにはDBの接続設定をがうまくできていることが必要がある(DBの接続設定はconfig/app.php内で設定可能)

今回はサンプルとして以下のような、idnamepriceカラムを持つBookTitleテーブルを使用する。

スクリーンショット 2014-12-05 20.01.11.png

スクリーンショット 2014-12-05 19.59.36.png

Tableオブジェクトはsrc/Model/Table/以下に追加する。


src/Model/Table/BookTitleTable.php

<?php 

namespace App\Model\Table;

use Cake\ORM\Table;

class BookTitleTable extends Table {

public function initialize(array $config) {
$this->table('BookTitle');
}

}


次にControllerから、このTableオブジェクトを使って、データを取得するには次のようにする。

ここで注意したいのが$rowの型はCake標準のCake\ORM\Entityになっている点(後で出てくるEntityと関係してくる)。


src/Controller/BookTitlesController.php

<?php 

namespace App\Controller;

use Cake\ORM\TableRegistry;

class BookTitlesController extends AppController {

public function index()
{
$tableBookTitles = TableRegistry::get('BookTitles');
$query = $tableBookTitles->find();
foreach ($query as $row) {
debug($row); // $rowの型はCake\ORM\Entity
echo $row->name;
}
}
}



Entity

Tableがオブジェクトがデータコレクションへのアクセスを提供する一方、Entityはそれぞれ個々のデータを表し、それらの持つデータにアクセスおよび操作することができる。

Entityはsrc/Model/Entity/以下に追加する。


src/Model/Entity/BookTitle.php

<?php 

namespace App\Model\Entity;

use Cake\ORM\Entity;

class BookTitle extends Entity {

/**
* 税込の価格を取得
*/

public function getPriceWithTax() {
return ceil($this->price * 1.08);
}

}


BookTitleのEntityを追加した後で、先ほどTableオブジェクトの項目で確認したBookTitlesController内の$rowの型を調べてみると、Cake\ORM\Entityだったものが、今回追加したApp\Model\Entity\BookTitleに変わる。

これにより、以下のようにTableオブジェクトから取得したBookTitleのEntityに対して自分でつくったメソッドを呼べるようになる。


src/Controller/BookTitlesController.php

<?php 

namespace App\Controller;

use Cake\ORM\TableRegistry;

class BookTitlesController extends AppController {

public function index()
{
$tableBookTitles = TableRegistry::get('BookTitles');
$query = $tableBookTitles->find();
foreach ($query as $row) {
debug($row); // $rowの型がApp\Model\Entity\BookTitleに変わる!
echo $row->getPriceWithTax(); // 独自のメソッドが呼べる!
}
}
}


このようにCakePHP3のModelでは、TableオブジェクトとEntityを連携させながらデータの取得および操作ができる。