49
51

More than 1 year has passed since last update.

CakePHPで知ってると便利なコードまとめ

Last updated at Posted at 2020-02-06

よく使うものをまとめています。随時更新。
※最新版では変更になっている場合があります。

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にする

Controller/JsonController.php
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

Model/Table/UsersTable.php
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の仮想フィールドとは違い、フェッチした結果に対してセットする

Model/Entity/User.php
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'];
    }
}

カスタムファインダー

Model/Table/GroupsTable.php
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;
}
Controller/UsersController.php
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を読み込み

Controller/Component/HogeComponent.php
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
Controller/UsersController.php
// 取得
$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

Command/OrderCommand.php
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に読み込む

config/const.php
use Cake\Core\Configure;

return [
    define('YEAR_MIN', 1980),
    define('YEAR_MAX', date('Y', strtotime('+1 year'))),
    define('VERSION', Configure::version())
];
config/bootstrap.php
try {
    Configure::config('default', new PhpConfig());
    Configure::load('app', 'default', false);
    Configure::load('const', 'default'); // 追加
} catch (\Exception $e) {
    exit($e->getMessage() . "\n");
}

ドット(.)区切りで多次元配列を取得

Helperで使うと便利かもしれない

View/Helper/HogeHelper.php
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);
}
View/Users/index.php
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();
49
51
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
51