## CakePHP はどんなフレームワーク?(森谷)
Ruby on Railsの概念の多くを取り入れており、Rails流の高速開発とPHPの機動性を兼ね備えたフレームワークと言われている 。
日本においては、2009年以降、2017年までもっとも人気のあるPHPフレームワークであり、2020年までは日本での稼働WEBサイト数はLaravelより多かったという調査結果もある。
■特徴
- bakeコマンドによるプログラム自動生成機能
- bakeコマンドを実行すると対話的に次々と入力を求められ、それに答えていくことでMVCモデルに沿ってのモデル/ビュー/コントローラのPHPプログラムを自動生成する機能
- MVCアーキテクチャ。
- 高い後方互換性。
- 下位バージョンからのアップグレードをサポートする公式移行ガイド及びUpgrade shell。
- 統合された柔軟なO/Rマッピング。
- MySQL、PostgreSQL、SQLite、Microsoft SQL Serverを標準サポート。
- フォームバリデーション機能。
- セキュリティ対策機能(XSS対策・CSRF対策・フォーム改竄検知)。
- カスタムURLを実現するためのリクエストディスパッチャー。
- PEAR等の外部ライブラリに依存しておらず、単体での利用が可能。
- 必要なライブラリをその都度利用できるインポート機能。
- プラグインによる機能拡張。
- 柔軟なビュー機構。
- テンプレートの継承や拡張。
- ページ単位及び部品単位のキャッシュ。
- Composerへの標準対応(バージョン3以降)。
## シェア
Googleトレンド
## モデル/MVC(加納)
![](https://hackmd.io/_uploads/B10N8uX53.png)
1. アプリケーションのミドルウェアが初期化される
1. リクエストとレスポンスが PSR-7 ミドルウェアを経由して ディスパッチされる
1. コントローラーとアクションが選択される
1. コントローラーがモデルとコンポーネントと通信する
1. コントローラーが出力をレスポンスに渡す
1. ビューがヘルパーとセルを使ってボディーとヘッダーを生成する
1. レスポンスが、 ミドルウェア を経由して返る
## [インストール方法](https://book.cakephp.org/4/ja/installation.html)(久山)
### composer を利用したインストール方法
以下のコマンドを実行
```bash=
composer self-update && composer create-project --prefer-dist cakephp/app:"4.*" cakephp_project
```
上記のコマンドで `cakephp_project` というフォルダが作成される。
CakePHP はプロジェクトは以下の各種フォルダに対してアクセス権限が必要となるため、プロジェクトフォルダ配下で以下コマンドを実行する必要がある。
```bash=
HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1`
setfacl -R -m u:${HTTPDUSER}:rwx tmp
setfacl -R -d -m u:${HTTPDUSER}:rwx tmp
setfacl -R -m u:${HTTPDUSER}:rwx logs
setfacl -R -d -m u:${HTTPDUSER}:rwx logs
```
CakePHP はビルドインサーバで起動して動作確認する場合でも、PDOでDB接続するため、インストール先のサーバにはDBがインストールされている必要がある。
以下のいずれかのDBをインストールしてPDO経由でアクセスできる状態にしておく必要があります。
* サポートしているDB
* MySQL (5.6 以上)
* MariaDB (5.6 以上)
* PostgreSQL (9.4 以上)
* Microsoft SQL Server (2012 以上)
* SQLite 3
なお、デフォルトはMySQL(テストはSqlite)が設定されている。
DBを変更する場合は `config/app.php` を以下の通り変更する。
(PostgreSQL に変更する場合)
```php=
<?php
use Cake\Cache\Engine\FileEngine;
use Cake\Database\Connection;
// use Cake\Database\Driver\Mysql;
use Cake\Database\Driver\Postgres;
use Cake\Log\Engine\FileLog;
use Cake\Mailer\Transport\MailTransport;
// 省略
'default' => [
'className' => Connection::class,
// 'driver' => Mysql::class,
'driver' => Postgres::class,
'persistent' => false,
'timezone' => 'UTC',
// 省略
```
[DB接続に関する参考情報](https://book.cakephp.org/4/ja/orm/database-basics.html#database-configuration)
`/bin/cake.php` で様々なコマンドが実行可能。
```bash=
App:
- help
Bake:
- bake
- bake all
- bake behavior
- bake cell
- bake command
- bake command_helper
- bake component
- bake controller
- bake controller all
- bake fixture
- bake fixture all
- bake form
- bake helper
- bake mailer
- bake middleware
- bake model
- bake model all
- bake plugin
- bake shell_helper
- bake template
- bake template all
- bake test
Cake/TwigView:
- twig-view compile
CakePHP:
- cache clear
- cache clear_all
- cache list
- completion
- i18n
- i18n extract
- i18n init
- plugin assets copy
- plugin assets remove
- plugin assets symlink
- plugin load
- plugin loaded
- plugin unload
- routes
- routes check
- routes generate
- schema_cache build
- schema_cache clear
- server
- version
DebugKit:
- benchmark
Migrations:
- bake migration
- bake migration_diff
- bake migration_snapshot
- bake seed
- bake simple_migration
- migrations
- migrations create
- migrations dump
- migrations mark_migrated
- migrations migrate
- migrations orm-cache-build
- migrations orm-cache-clear
- migrations rollback
- migrations seed
- migrations status
```
プロジェクトフォルダ配下で `./bin/cake server` を実行することでビルドインサーバを起動させることができる。
![](https://hackmd.io/_uploads/HJ8as_W5n.png)
## [ルーティング](https://book.cakephp.org/4/ja/development/routing.html)(久山)
### 基本設定
`config/routes.php` 配下にルーティングの設定を記載。
```php=
$routes->connect('test', ['controller' => 'Tests', 'action' => 'index']);
```
上記を記載することで、`/test` にアクセスした際に `TestsController.php` の `index` メソッドにルーティングさせることができる。
### URLパラメータの受け取り
```php=
$routes->connect(
'/articles/{id}',
['controller' => 'Articles', 'action' => 'view'],
)
->setPatterns(['id' => '\d+'])
->setPass(['id']);
```
上記で `articles/15` のようなリクエストを受けた際にコントローラーに引数として渡すことができる。
### DRYに保つための scope 設定
```php=
$routes->scope('/test', function (RouteBuilder $routes) {
$routes->connect('/', ['controller' => 'Tests', 'action' => 'index']);
// Using fluent interface
$routes->connect(
'/articles/{id}',
['controller' => 'Articles', 'action' => 'view'],
)
->setPatterns(['id' => '\d+'])
->setPass(['id']);
});
```
上記のようにルーティング設定を記載することで、`/test` までのリクエストをグループ化することができる。
`articles` にアクセスする際は `/test/articles` となる。
また、[Middleware](https://book.cakephp.org/4/ja/controllers/middleware.html) 設定も共通化することができるので、同様の設定を複数個所に記述しなくてもよくなる。
## [リクエスト管理](https://book.cakephp.org/4/ja/controllers/request-response.html)(久山)
`Cake\Http\ServerRequest` 経由でリクエスト情報にアクセスすることができる。
```php=
// ルートパラメータを受け取る場合
$controllerName = $this->request->getParam('controller');
// GET のパラメータを受け取る場合
$page = $this->request->getQuery('page');
// GET のパラメータにデフォルト値を設定することも可能
$foo = $this->request->getQuery('does_not_exist', 'default val');
// POST のパラメータを受け取る場合
$value = $this->request->getData('address.street_name');
```
## [セッション管理](https://book.cakephp.org/4/ja/development/sessions.html#accessing-session-object)(久山)
リクエストオブジェクトには以下からアクセスできる
* Controllers
* Views
* Helpers
* Cells
* Components
基本的なセッションの使用例は以下の通り
```php=
// セッションの書き込み
$session = $this->request->getSession();
$session->write('session_val', 'value');
// セッションの取得
$name = $this->request->getSession()->read('session_val');
// セッションの削除
$session->delete('session_val');
// セッションの存在確認
if ($session->check('session_val')) {
// session_val に値が設定されているか
}
// セッションの破棄
$session->destroy();
```
## ORM(浅野)
https://book.cakephp.org/4/ja/orm/database-basics.html
### 接続
データベース接続を作る1番簡単な方法は`DNS`文字列を使います。
```php
use Cake\Datasource\ConnectionManager;
$dsn = 'mysql://root:password@localhost/my_database';
ConnectionManager::setConfig('default', ['url' => $dsn]);
```
作成したら、コネクションオブジェクトにアクセスして使えるようになります。
```php
$connection = ConnectionManager::get('default');
```
#### サポートしてるデータベース
下記のリレーショナルデータベースをサポートしている
- MySQL 5.1+
- SQLite 3
- PostgreSQL 8.3+
- SQLServer 2008+
- Oracle (コミュニティープラグイン経由)
### select
生のSQLクエリーの実行が簡単
接続を取得して、SQLを実行する
fetch: 1行取得
fetchAll: 全行取得
```php
use Cake\Datasource\ConnectionManager;
$connection = ConnectionManager::get('default');
$results = $connection->execute('SELECT * FROM articles')->fetchAll('assoc');
```
#### assocとは
読み込んだフェッチする時には、2つのモードを使用することができます。 結果配列のキーを項目の通番にする場合 (num) と、項目名をキーにする場合 (assoc) です。
これは引数として複合データ型を使用することも可能です。
```php
use Cake\Datasource\ConnectionManager;
use DateTime;
$connection = ConnectionManager::get('default');
$results = $connection
->execute(
'SELECT * FROM articles WHERE created >= :created',
['created' => new DateTime('1 day ago')],
['created' => 'datetime']
)
->fetchAll('assoc');
```
クエリビルダもある
```php
$results = $connection
->newQuery()
->select('*')
->from('articles')
->where(['created >' => new DateTime('1 day ago')], ['created' => 'datetime'])
->order(['title' => 'DESC'])
->execute()
->fetchAll('assoc');
```
### insert
下記のようにデータをインサートします
```php
use Cake\Datasource\ConnectionManager;
use DateTime;
$connection = ConnectionManager::get('default');
$connection->insert('articles', [
'title' => 'A New Article',
'created' => new DateTime('now')
], ['created' => 'datetime']);
```
### update
id=10のデータを更新する例
```php
use Cake\Datasource\ConnectionManager;
$connection = ConnectionManager::get('default');
$connection->update('articles', ['title' => 'New title'], ['id' => 10]);
```
### delete
`delete()`を使ってデータベースから行を削除する
id=10のデータを削除する例
```php
use Cake\Datasource\ConnectionManager;
$connection = ConnectionManager::get('default');
$connection->delete('articles', ['id' => 10]);
```
### トランザクション
トランザクションの開始、コミット、ロールバックを手動でやる方法
`begin()`トランザクションの開始
`commit()`コミット
`rollback()`ロールバック
```php
$connection->begin();
$connection->execute('UPDATE articles SET published = ? WHERE id = ?', [true, 2]);
$connection->execute('UPDATE articles SET published = ? WHERE id = ?', [false, 4]);
$connection->commit();
```
よしなにやってくれる方法
```php
$connection->transactional(function ($connection) {
$connection->execute('UPDATE articles SET published = ? WHERE id = ?', [true, 2]);
$connection->execute('UPDATE articles SET published = ? WHERE id = ?', [false, 4]);
});
```
transactional()の動き
- begin を呼び出します。
- 引数で渡されたクロージャーを実行します。
- もしクロージャー内で例外が発生したら、ロールバックを発行して例外を再度 throw します。
- クロージャーが false を返したら、ロールバックを発行して false を返します。
- クロージャーが正常終了したら、トランザクションをコミットします。
## エラーハンドリング(堀川)
https://book.cakephp.org/4/en/development/errors.html#error-exception-handling
### エラーと例外の発生
`config/app.php`でエラーの設定を行う。PHPエラーのハンドリングには`Cake\Error\ErrorTrap`、例外のハンドリングには`Cake\Error\ExceptionTrap`を使用する。
#### サポートしているオプション
- errorLevel:int
取得するエラーレベルの選択
- trace:int
スタックトレースをログファイルに含めるかどうか
- exceptionRenderer:string
未補足例外のレンダリングを行うクラスの指定
指定したクラスは`src/Error`に置き、`render()`メソッドを実装する必要がある
- log:bool
例外とスタックトレースを`Cake\Log\Log`に記録するかどうか
- skipLog:array
ログに記録しない例外クラス名の配列
- extraFatalErrorMemory:int
fatalエラー発生時に上げるメモリ制限を設定
ロギングやエラー処理を完了するためのメモリ的余裕を作る
- logger:Cake\Error\ErrorLoggerInterface
エラーと未補足例外のロギングを行うクラスの指定
デフォルトは`Cake\Error\ErrorLogger`
- errorRenderer:Cake\Error\ErrorLoggerInterface
エラーのレンダリングクラスを設定
デフォルトはPHP SAPIに基づいて選択される
- ignoredDeprecationPaths:array
無視したいdeprecationエラーのglob互換パスリスト
4.2.0で追加
### DeprecationWarning
`deprecationWarning()`を使用して非推奨警告を出すことが可能
```php
deprecationWarning('The example() method is deprecated. Use getExample() instead.');
```
### 例外処理の変更
#### Listen to Events
CakePHPのイベントを通じてエラーと例外を通知
ErrorTrapとExceptionTrapがCakePHPのイベントをトリガーする
Error.beforeRenderイベントのリッスンでPHPエラーの通知を受け取る
Exception.beforeRenderイベントが例外処理時にdispatchされる
```php
$errorTrap = new ErrorTrap(Configure::read('Error'));
$errorTrap->getEventManager()->on(
'Error.beforeRender',
function (EventInterface $event, PhpError $error) {
// do your thing
}
);
```
#### Custom Error Templates
デフォルトのエラーハンドラは`Cake\Error\ExceptionRenderer`と`ErrorController`を使用して未補足例外をキャッチする
エラーページは`templates/Error/`にあり、全ての4xxエラーは`error400.php`を、全ての5xxエラーは`error500.php`を使用する
##### 使用できる変数
- message:例外メッセージ
- code:エラーコード
- url:リクエストURL
- error:例外オブジェクト
##### Custom Error Page Layout
デフォルトは`templates/layout/error.php`を使用
`layout`プロパティを使用して別のレイアウトを使用できる
```php
// inside templates/Error/error400.php
$this->layout = 'my_error';
```
#### Custom ErrorController
`App\Controller\ErrorController`クラスが例外レンダリングするために使われ、このクラスを変更することで、どのコンポーネントを使用し、どのテンプレートをレンダリングするか制御できる
```php
namespace App\Controller\Admin;
use App\Controller\AppController;
use Cake\Event\EventInterface;
class ErrorController extends AppController
{
/**
* beforeRender callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return void
*/
public function beforeRender(EventInterface $event)
{
$this->viewBuilder()->setTemplatePath('Error');
}
}
```
#### Custom ExceptionRenderer
例外のレンダリングとロギングの処理全体を制御したい場合は、`config/app.php`の`Error.exceptionRenderer`オプションで例外ページをレンダリングするクラスを選択する
```php
// In src/Error/AppExceptionRenderer.php
namespace App\Error;
use Cake\Error\ExceptionRenderer;
class AppExceptionRenderer extends ExceptionRenderer
{
public function missingWidget($error)
{
$response = $this->controller->getResponse();
return $response->withStringBody('Oops that widget is missing.');
}
}
// In config/app.php
'Error' => [
'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
// ...
],
// ...
```
#### Changing the ErrorController Class
例外のレンダリングに使用するコントローラを変更したい場合は、例外rendarerの`_getController()`メソッドをオーバライドする
```php
// in src/Error/AppExceptionRenderer
namespace App\Error;
use App\Controller\SuperCustomErrorController;
use Cake\Controller\Controller;
use Cake\Error\ExceptionRenderer;
class AppExceptionRenderer extends ExceptionRenderer
{
protected function _getController(): Controller
{
return new SuperCustomErrorController();
}
}
// in config/app.php
'Error' => [
'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
// ...
],
// ...
```
#### Custom Error Logging
Erroe.errorLoggerの設定値を使用してエラーロガーを置き換えられる
```php
namespace App\Error;
use Cake\Error\ErrorLoggerInterface;
use Cake\Error\PhpError;
use Psr\Http\Message\ServerRequestInterface;
use Throwable;
/**
* Log errors and unhandled exceptions to `Cake\Log\Log`
*/
class ErrorLogger implements ErrorLoggerInterface
{
/**
* @inheritDoc
*/
public function logError(
PhpError $error,
?ServerRequestInterface $request,
bool $includeTrace = false
): void {
// Log PHP Errors
}
/**
* @inheritDoc
*/
public function logException(
?ServerRequestInterface $request,
bool $includeTrace = false
): void {
// Log exceptions.
}
}
```
#### Custom Error Rendering
デフォルトで、CakePHPにはWeb環境とコンソール環境の両方に対応したエラーレンダラが同梱されているが、レンダリングロジックを置き換えたい場合はクラスを作成することも可能
```php
// src/Error/CustomErrorRenderer.php
namespace App\Error;
use Cake\Error\ErrorRendererInterface;
use Cake\Error\PhpError;
class CustomErrorRenderer implements ErrorRendererInterface
{
public function write(string $out): void
{
// output the rendered error to the appropriate output stream
}
public function render(PhpError $error, bool $debug): string
{
// Convert the error into the output string.
}
}
```
#### Creating your own Application Exceptions
SPLに組み込まれている例外、Exceptionそのもの、`Cake\Core\Exception\Exception`のいずれかを使用して、独自のアプリケーション例外を作成できる
`Cake\Core\Exception\Exception`のコンストラクタでは、追加データを渡すことができる。この追加データは`_messageTemplate`に補間される。これにより、エラーの周囲により多くのコンテキストを提供する、データリッチな例外を作成できる
```php
use Cake\Core\Exception\CakeException;
class MissingWidgetException extends Exception
{
// Context data is interpolated into this format string.
protected $_messageTemplate = 'Seems that %s is missing.';
// You can set a default exception code as well.
protected $_defaultCode = 404;
}
throw new MissingWidgetException(['widget' => 'Pointy']);
```
#### Logging Exceptions
組み込みの例外処理を使用して、`config/app.php`でlogオプションをtrueに設定することで、ErrorTrapによって処理されるすべての例外をログに記録できる
これを有効にすると、すべての例外が`Cake\Log\Log`と設定されたロガーにログ記録される
## [プラグイン](https://book.cakephp.org/3/ja/plugins.html)(廣部)
### 概要
CakePHP では、コントローラー・モデル・ビューの組み合わせを設定し、 他の CakePHP アプリケーションで使用できるよう事前にパッケージ化された アプリケーションプラグインとしてリリースできます。
CakePHP プラグインは、ホストアプリケーション自身とは基本的に分離されており、 一般的にきちんとパッケージ化され、他のアプリケーションではほとんど手間をかけずに 再利用できる明確な機能を提供します。アプリケーションとプラグインは、それぞれの空間で動作しますが、 アプリケーションの設定によって定義・共有される、アプリケーション固有のプロパティー (データベース接続パラメーターなど)を共有します。
CakePHP 3.0 では、それぞれのプラグインごとに最上位の名前空間 (例えば DebugKit と いったように) を定義します。規約により、プラグインはそのパッケージ名が名前空間の名前と なります。 もし別の名前空間名を使いたいなら、プラグインをロードする時に設定することが可能です。
### インストール
Composer を使ったプラグインのインストールと、手動インストールの2種類がある
- Composerを使ったインストール(省略)
- 手動インストール
1. プラグインのコードを plugins ディレクトリーにコピーする
2. アプリケーションの composer.json ファイルを次の情報を含むように更新する
```json=
{
"autoload": {
"psr-4": {
"MyPlugin\\": "plugins/MyPlugin/src/"
}
},
"autoload-dev": {
"psr-4": {
"MyPlugin\\Test\\": "plugins/MyPlugin/tests/"
}
}
}
```
### プラグインの読み込み
プラグインのルート、コンソールコマンド、ミドルウェア、またはイベントリスナーが欲しい場合、 プラグインを読み込む必要があります。プラグインは、アプリケーションの bootstrap() 関数の中で 読み込まれます。
```php=
// src/Application.php の中。3.6.0 以上が必要。
use Cake\Http\BaseApplication;
use ContactManager\Plugin as ContactManagerPlugin;
class Application extends BaseApplication {
public function bootstrap()
{
parent::bootstrap();
// クラス名で contact manager プラグインを読み込み
$this->addPlugin(ContactManagerPlugin::class);
// '短縮名' でベンダーの名前空間付きプラグインを読み込み
$this->addPlugin('AcmeCorp/ContactManager');
}
}
```
単にプラグインのヘルパー、ビヘイビアー、またはコンポーネントが欲しいだけの場合、 プラグインを読み込む必要はありません。
3.6.0 より前の場合、 Plugin::load() を使ってください。
```php=
// config/bootstrap.php の中で
// 特定のプラグインを読み込みます。
Plugin::load('ContactManager');
// ベンダーの名前空間の特定のプラグインを読み込みます。
Plugin::load('AcmeCorp/ContactManager');
```
また、プラグインを有効にする便利なシェルコマンドがあります。次の行を実行してください。
```shell=
bin/cake plugin load ContactManager
```
これは、アプリケーションの bootstrap メソッドを更新、 または `$this->addPlugin('ContactManager');` を bootstrap に書き込みます。
### 有名所のプラグイン
- GitHubでCakePHP Pluginで調べて、Star順に並び替えたやつ
https://github.com/search?q=CakePHP+plugin&type=repositories&s=stars&o=desc
- [awesome-cakephp](https://github.com/FriendsOfCake/awesome-cakephp)
A curated list of amazingly awesome CakePHP 4.x+ plugins, resources and shiny things.
驚くほど素晴らしいCakePHP 4.x 以降のプラグイン、リソース、魅力的なものの厳選されたリスト。
- CakeDCシリーズ
Cake Development Corporationが作ったプラグイン?
- [CakeDC/users](https://github.com/CakeDC/users)
- ユーザの登録、ログイン・ログアウト系のプラグイン
- [CakeDC/search](https://github.com/CakeDC/search)
- This Search plugin enables developers to quickly implement the POST-Redirect-GET pattern.
- [webtechnick/CakePHP-Facebook-Plugin](https://github.com/webtechnick/CakePHP-Facebook-Plugin)
- FaceBookのAPIへのラッパー?
- メンテされてなさそうだけど、スター数は多い
- [FriendsOfCake/CakePdf](https://github.com/FriendsOfCake/CakePdf)
- HTMLをPDFに変換してくれるプラグイン
## セキュリティ(加納)
## 3→4のバージョンアップ