よく使うものをまとめています。随時更新。
※最新版では変更になっている場合があります。
CakePHP:ver3.7.6
PHP:7.2.3
Cookbook
https://book.cakephp.org/3/ja/
4.x版はこちら
https://qiita.com/egcmrsk/items/09417521c7f0ac5f210f
Controller
リクエスト
現在のルートを取得
$this->request->getParam();
// controller
$this->request->getParam('controller');
// action
$this->request->getParam('action');
// prefix
$this->request->getParam('prefix');
リクエストの状態をチェック
リクエストの種類は以下を参照
https://book.cakephp.org/3/ja/controllers/request-response.html#check-the-request
$this->request->is(['post', 'put']);
リクエストを取得
// POST
$this->request->getData();
$this->request->getData('mode'); // $_POST['mode']
// GET
$this->request->getQuery();
$this->request->getQuery('mode'); // $_GET['mode']
リクエストをセット
// POST
$this->request = $this->request->withData('year', date('Y'));
$this->request = $this->request->withData('month', date('n'));
// GET
$this->request = $this->request->withQueryParams([
'year' => date('Y'),
'month' => date('n')
]);
HTTPの例外を追加(Exception)
例外の種類は以下を参照
https://book.cakephp.org/3/ja/development/errors.html#http
use Cake\Http\Exception\NotFoundException;
if (!$item) {
throw new NotFoundException(__('アイテムが見つかりません'));
}
フォームの値を変更可にする(Securityコンポーネント使用時)
JavaScriptでフォームの値を変更して送信したいとき、Securityコンポーネントを有効にしているとエラーが出てしまう。
そういうときは変更したいもののみ許可する。
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
// addメソッド内で、modeの値の変更を許可
if ($this->request->getParam('action') == 'add') {
$this->Security->setConfig('unlockedFields', ['mode']);
}
}
Sessionの読み書き
$this->request->session()->read('calendar');
$this->request->session()->write('calendar', $calendar);
Cacheの読み書き
use Cake\Cache\Cache;
$calendar = Cache::read('calendar');
if ($calendar == false) {
$calendar = $this->Calendars->find()->toArray();
Cache::write('calendar', $calendar);
}
リファラ
$referer = $this->referer();
// リダイレクトにそのまま入れられます
return $this->redirect($referer);
JSONを出力する
JSON用のControllerを用意し、出力はすべてJSONにする
class JsonController extends AppController
{
public function initialize()
{
parent::initialize();
$this->viewBuilder()->className('Json');
}
public function index()
{
$this->loadModel('Products');
$products = $this->Products->find()
->where(['enabled' => 1])
->order(['id' => 'desc'])
->toArray();
$this->set(['json' => $products, '_serialize' => 'json']);
}
}
Model
登録したレコードのIDを取得
save後にEntityにセットされる
$entity = $this->Model->newEntity();
$entity = $this->Model->patchEntity($entity, $this->request->getData());
$this->Model->save($entity);
$id = $entity->id; // ID取得
バリデーション
バリデーションのルールを追加したい場合
https://book.cakephp.org/3/ja/core-libraries/validation.html#id5
public function validationDefault(Validator $validator)
{
...
$validator
->scalar('username')
->maxLength('username', 255)
->allowEmptyString('username')
// 文字数チェック
->add('username', 'length', [
'rule' => ['minLength', 6],
'message' => 'ユーザー名は6文字以上必要です'
])
// 重複チェック
// 動的なルールも設定できます
->add('username', 'unique', [
'rule' => function ($value, $context) {
$condition = [
'username' => $value
];
if (isset($context['data']['id']) && $context['data']['id']>0) {
$condition['id !='] = $context['data']['id'];
}
$exist = $this->find()->where($condition)->first();
return $exist ? false : true;
},
'message' => 'ユーザー名はすでに使用されています'
]);
}
仮想フィールド(virtualFields)
CakePHP2の仮想フィールドとは違い、フェッチした結果に対してセットする
class User extends Entity {
protected $_virtual = ['name'];
protected function _getName() {
// last_name、first_nameをひとつにまとめてセット
if (!isset($this->_properties['last_name']) || !isset($this->_properties['first_name'])) {
return null;
}
return $this->_properties['last_name'] . ' ' . $this->_properties['first_name'];
}
}
カスタムファインダー
public function findSelection($query, $options)
{
$query->find('all')
->contain(['ParentGroups'])
->where([
'Groups.enabled' => 1
])
->order([
'Groups.lft' => 'asc',
'Groups.id' => 'asc'
]);
// Collectionを使って親グループ+子グループの名前を返す
$collection = new Collection($query);
$new = $collection->map(function ($entity) {
$name = '';
if ($entity->parent_group) {
$name .= '[' . $entity->parent_group->name . '] ';
}
$name .= $entity->name;
return [
'id' => $entity->id,
'name' => $name
];
});
return $new;
}
class UsersController extends AppController {
public function initialize() {
parent::initialize();
$this->loadModel('Groups');
}
public function add() {
...
$groups = $this->Groups->find('selection')->toArray();
// [
// [id => 1, name => '[親グループ名] グループ名'],
// [id => 2, name => 'グループ名']
// ]
}
}
Component / Helper
Configのセット、取得
// セット
$holidays = [
'2020-01-01',
'2020-01-13',
'2020-02-11',
'2020-02-24',
...
];
$this->setConfig('holidays', $holidays);
// 取得
$holidays = $this->getConfig('holidays');
##Modelを読み込み
use Cake\Datasource\ModelAwareTrait;
class HogeComponent extends Component {
use ModelAwareTrait;
public function method() {
$this->loadModel('Model');
}
}
Componentで他のComponentを使う
public $components = ['Calendar'];
$calendar = $this->Calendar->build(2020);
ControllerまたはComponent内でHelperを使う
自作したHelperの場合はパスに注意
// Urlヘルパーを使う
use Cake\View\Helper\UrlHelper;
$Url = new UrlHelper(new \Cake\View\View());
$url = $Url->build('/', true); // http://hogehoge.com/
// 自作したヘルパーを使う
use App\View\Helper\UtilHelper;
$Util = new UtilHelper(new \Cake\View\View());
$password = $Util->createPassword(); // UtilヘルパーのcreatePasswordを実行
ComponentからControllerへアクセス
$controller = $this->_registry->getController();
ComponentでSessionを扱う
Controllerを通して取得・編集できます
// Controllerにアクセス
$controller = $this->_registry->getController();
// Controllerからセッションにアクセス
$session = $controller->getRequest()->getSession();
// あとはController内でセッションを扱うときと一緒
$session->read('hogehoge');
View
レイアウトを変更する
src/Template/Layout/front.phpをレイアウトに指定したい場合
// Controllerのメソッドから
public function view()
{
...
$this->viewBuilder()->setLayout('front');
}
// ビューファイルから
$this->layout = 'front';
データの取得
任意のカラムで取得
いまいち使いどころがわからなかったんですが、存在チェックとかでさくっと取りたいときはいいかもしれません。
// 「username」が一致するものを取得
$user = $this->Users->findByUsername('hogehoge');
// ORも使える
$user = $this->Users->findAllByUsernameOrEmail('hogehoge', 'hogehoge@hoge.com');
// 結果取得
$user->first();
クエリが空か判定
$query = $this->Users->find()->where(['enabled' => 1, 'email' => 'hogehoge@hoge.com']);
$query->isEmpty(); // trueまたはfalse
データの整形
データ整形はとりあえずCollectionに突っ込んでいたのですが、そもそもクエリーはCollectionオブジェクトだそうで、直接extractとかcombineとかできるらしい(もっと早く知りたかった)
https://book.cakephp.org/3/ja/orm/query-builder.html#collection
$query = $this->Users->find()->where(['enabled' => 1]);
// IDと名前の配列
$users = $query->combine('id', 'name')->toArray();
// [1 => 'ユーザー1', 2 => 'ユーザー2', 3 => 'ユーザー3']
// IDのみの配列
$users = $query->extract('id')->toArray();
// [1, 2, 3]
データの保存
アソシエーションを一緒に保存
ユーザー | 中間テーブル | グループ |
---|---|---|
Users | UsersGroups | Groups |
UsersとGroupsでbelongsToManyを設定しておく | ||
https://book.cakephp.org/3/ja/orm/associations.html#belongstomany |
// 取得
$id = 1;
$user = $this->Users->get($id, ['contain' => 'Groups']);
// 保存
$data = [
'id' => 1,
'name' => 'hogehoge',
'groups' => [
'_ids' => [1, 2, 3] // GroupsのIDを配列で指定
]
];
$user = $this->Users->patchEntity($user, $data, ['associated' => ['Groups']]);
$this->Users->save($user);
複数データを保存
複数レコードの変換
https://book.cakephp.org/3/ja/orm/saving-data.html#id9
$data = [
['name' => '北海道', 'country' => 'JP'],
['name' => '東北', 'country' => 'JP'],
['name' => '関東', 'country' => 'JP'],
['name' => '中部', 'country' => 'JP'],
['name' => '近畿', 'country' => 'JP'],
['name' => '中国・四国', 'country' => 'JP'],
['name' => '九州・沖縄', 'country' => 'JP']
];
$entities = $this->Model->newEntities($data);
// 個別のトランザクションで処理
foreach ($entities as $entity) {
$this->Model->save($entity);
}
// 単一トランザクションで処理
$this->Model->getConnection()->transactional(function () use ($entities) {
foreach ($entities as $entity) {
$this->Model->save($entity, ['atomic' => false]);
}
});
データを一気に更新
// statusが「2」のものをすべて「1」にする
$this->Model->updateAll(['status' => 1], ['status' => 2]);
エンティティ
データの保存前にエラーをチェック
patchEntityを実行後、hasErrors、errorsで確認できる
$entity = $this->Model->newEntity();
$entity = $this->Model->patchEntity($entity, $this->request->getData());
if ($entity->hasErrors()) {
$this->Flash->error(__('エラーがあります'));
// エラーの詳細を表示
$errors = '';
foreach ($entity->errors() as $key => $value) {
if (is_array($value)) {
foreach ($value as $k => $v) {
$errors .= "[{$key}] {$v}\n";
}
} else {
$errors .= "[{$key}] {$value}\n";
}
}
$this->set(compact('errors'));
} else {
$this->Model->save($entity);
}
エラーをセット
バリデーションルール以外にチェックしたい項目があるときや、エラーがあった場合の動作チェックをしたいときに便利
$entity = $this->Model->newEntity();
$entity = $this->Model->patchEntity($entity, $this->request->getData());
$entity->setError('required', ['必須項目です']);
if ($entity->hasErrors()) {
// エラーがあった場合の動作
...
}
DB関連
SQL文を直接実行
use Cake\Datasource\ConnectionManager;
$db = ConnectionManager::get('default');
$data = $db->execute('SELECT * FROM hoge Hoge WHERE enabled = ?', [1]);
実行したSQL文を確認
$this->Model->getDataSource()->getLog();
Command
コマンドに引数を設定
対話形式にしたい場合など、引数を設定して条件分岐できます。
https://book.cakephp.org/3/ja/console-and-shells/commands.html
protected function buildOptionParser(ConsoleOptionParser $parser)
{
$parser->addArgument('id', [
'help' => 'order id.',
'required' => false
]);
return $parser;
}
public function execute(Arguments $args, ConsoleIo $io)
{
$id = $args->getArgument('id');
if ($id) {
// id があった場合の処理
} else {
// それ以外の処理
}
}
$ bin/cake order 200
# '200'が渡される
その他
定数設定
定数用ファイルを別途用意し、CakePHPに読み込む
use Cake\Core\Configure;
return [
define('YEAR_MIN', 1980),
define('YEAR_MAX', date('Y', strtotime('+1 year'))),
define('VERSION', Configure::version())
];
try {
Configure::config('default', new PhpConfig());
Configure::load('app', 'default', false);
Configure::load('const', 'default'); // 追加
} catch (\Exception $e) {
exit($e->getMessage() . "\n");
}
ドット(.)区切りで多次元配列を取得
Helperで使うと便利かもしれない
use Cake\Utility\Hash;
protected $_defaultConfig = [
'users' => [
'status' => [1 => '仮会員', 2 => '会員']
],
'groups' => [
'status' => [1 => '有効', 2 => '無効']
]
];
public function get($arg = null) {
if (!$arg) {
return null;
}
$config = $this->_defaultConfig;
return Hash::get($config, $arg);
}
use Cake\Utility\Hash;
$this->Hoge->get('users.status'); // [1 => '仮会員', 2 => '会員']
$this->Hoge->get('groups.status'); // [1 => '有効', 2 => '無効']
$this->Hoge->get('users.status')[2]; // 会員
キーと値の配列を作る
use Cake\Utility\Hash;
$groups = $this->Groups->find()->toArray();
$groups = Hash::combine($groups, '{n}.id', '{n}.name');
値だけ欲しい場合はextract
use Cake\Utility\Hash;
$groups = $this->Groups->find()->toArray();
$groups = Hash::extract($groups, '{n}.id');
日時を扱う
Timeクラスを使うとapp.phpで設定したタイムゾーンに合わせてくれます
https://book.cakephp.org/3/ja/core-libraries/time.html
use Cake\I18n\Time;
// 現在時刻
$now = Time::now();
// インスタンスを作成して
$time = new Time();
// 足したり引いたり
$time->modify('-7 day');
// セットしたり
$time->year(2021)->month(1)->day(3);
$time->setDate(2021, 1, 3);
// 文字列から作成する場合は、YYYY-MM-DD HH:II:SSの形式で指定
$time = new Time('2021-04-01 12:30:30');
// タイムスタンプからも作成できます
$time = Time::createFromTimestamp(time());
// フォーマット
$time->format('Y/m/d');
バージョン
Configure::version();