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

[CakePHPで知ってると便利なコードまとめ](https://qiita.com/egcmrsk/items/5312e81a366c6a6a8209) の4.x版です。
3.xと違うものだけまとめています。随時更新。
※最新版では変更になっている場合があります。

CakePHP：ver4.1.6
PHP：7.4.11

Cookbook
https://book.cakephp.org/4/ja/

#Controller
##ログイン中のユーザーを取得
※ cakephp/authentication使用

```php
$this->request->getAttribute('identity'); // ユーザー情報のオブジェクト
$this->request->getAttribute('identity')->id; // ID
```

##JSONを出力する
jsonOptionsでjson_encodeのオプションを指定できる
https://book.cakephp.org/4/ja/views/json-and-xml-views.html
※2020/12/17修正

```php:Controller/JsonController.php
class JsonController extends AppController
{
    public function initialize(): void
    {
        parent::initialize();
        $this->loadComponent('RequestHandler');
        $this->viewBuilder()->setClassName('Json');
    }

    public function index()
    {
        $this->loadModel('Products');
        $products = $this->Products->find()
            ->where(['enabled' => 1])
            ->order(['id' => 'desc'])
            ->toArray();

        $this->set(compact('products'));

        // 配列を出力
        // $this->viewBuilder()
        //     ->setOption('serialize', ['products']);
        $this->set('_serialize', ['products']);

        // オブジェクトを出力
        // $this->viewBuilder()
        //    ->setOption('serialize', ['products'])
        //    ->setOption('jsonOptions', JSON_FORCE_OBJECT);
        $this->set('_jsonOptions', JSON_FORCE_OBJECT);
        $this->set('_serialize', ['products']);
    }
}
```

#Model
##仮想フィールド(virtualFields)
`$this->_properties[field]`から`$this->field`になりました

```php:Model/Entity/User.php
class User extends Entity {
    protected $_virtual = ['name'];

    protected function _getName() {
        // last_name、first_nameをひとつにまとめてセット
        if (!$this->last_name || !$this->first_name) {
            return null;
        }
        return $this->last_name . ' ' . $this->first_name;
    }
}
```

#認証(cakephp/authentication)
以下の手順で設定した状態です
https://book.cakephp.org/4/ja/tutorials-and-examples/cms/authentication.html

##メールアドレスまたはアカウントでログイン
複数のカラムを参照したい場合は配列で指定

```php:src/Application.php
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
    ...
    $authenticationService->loadIdentifier('Authentication.Password', [
        'fields' => [
            'username' => ['username', 'email'], // usernameまたはemailを参照
            'password' => 'password',
        ]
    ]);
}
```

```php:templates/Users/login.php
<div class="users form">
    <?= $this->Flash->render() ?>
    <h3>Login</h3>
    <?= $this->Form->create() ?>
    <fieldset>
        <legend><?= __('Please enter your username and password') ?></legend>
        <?php /* フォームの名前を「username」にしておく */ ?>
        <?= $this->Form->control('username', ['required' => true]) ?>
        <?= $this->Form->control('password', ['required' => true]) ?>
    </fieldset>
    <?= $this->Form->submit(__('Login')); ?>
    <?= $this->Form->end() ?>
    <?= $this->Html->link("Add User", ['action' => 'add']) ?>
</div>
```

##ModelとFinderを指定
認証時にFinderを通すことが出来ます

```php:src/Application.php
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
    ...
    $authenticationService->loadIdentifier('Authentication.Password', [
        'resolver' => [
            'className' => 'Authentication.Orm',
            'userModel' => 'Users',
            'finder' => 'auth',
        ]
    ]);
}
```

```php:src/Model/Table/UsersTable.php
public function findAuth($query, $options)
{
    // enabledの値が「1」のユーザーのみ許可
    $query->where([
        'enabled' => 1,
    ]);
    return $query;
}
```

#ミドルウェア
##一部でCSRF保護を無効にする
CSRF保護をある条件下でのみ無効にします。
CSFRについてはこちら
https://book.cakephp.org/4/ja/controllers/middleware.html#csrf-middleware

```php:src/Application.php
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
    $csrf = new CsrfProtectionMiddleware(['httpOnly' => true]);
    $csrf->skipCheckCallback(function ($request) {
        // prefixが「Api」の場合無効にする
        if ($request->getParam('prefix')=='Api') {
            return true;
        }
    });
    ...
    $middlewareQueue->add($csrf);
    return $middlewareQueue;
}
```

#プラグイン
##DebugKitを強制的に表示
localじゃないけどDebugKit出したい人:raised_hand:

```php:config/bootstrap.php
if (Configure::read('debug')) {
    ...
    Configure::write('DebugKit.forceEnable', true); //追加
}
```

```php:src/Application.php
public function bootstrap(): void
{
    ...
    if (Configure::read('debug')) {
        $this->addPlugin('DebugKit');
    }
}
```

app.phpまたはapp_local.phpでデバッグを切り替えれば表示されます。

```php:config/app_local.php
// trueにすると表示される
'debug' => filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN),
```

#その他
##定数設定
3.x版の書き方だとエラー出ます（当然といえば当然ですね…）
読み込み方は3.xと一緒。

※行末が「,」になっていました。正しくは「;」です

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

define('YEAR_MIN', 1980);
define('YEAR_MAX', date('Y', strtotime('+1 year')));
define('VERSION', Configure::version());

return []; // 必ず配列を返す
```

#メモ

- initializeはvoid関数
- Helperのoptionが変わっている