🍰【CakePHP2】フォームの入力値をリアルタイムにAjaxで非同期通信して検索結果を表示させる

## 環境

PHP 7.2.21
CakePHP 2.10.18

## やりたいこと

フォームのtextboxにIDを入力するとAjaxで非同期通信し、その値を元に検索して得たデータを表示したい

ID入力
![before.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188242/c601b185-4806-ccfa-a61a-1eff1a2a5caa.png)
↓
IDに紐づく値を隣に表示
![after.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188242/a9161be5-5c44-c83b-9c4f-181c893b4343.png)

## やったこと

ControllerにてAjax用の関数を追加しセキュリティを変更
Viewでは表示エリアとAjaxのJavascriptを追加

```Controller/HogeController.php
    /**
     * beforeFilter method
     *
     * @return void
     */
    public function beforeFilter(): void {
        parent::beforeFilter();

        // Ajax通信の為セキュリティを解除
        $this->Security->unlockedActions = ['fetchDBData'];
    }

    /**
     * 入力されたIDを元にAjaxで名前を取得するpublic関数
     */
    public function fetchDBData() {
        // 表示はせずにデータをやりとりする関数なのでView不要の記述
        $this->autoRender = false;

        if ($this->RequestHandler->isAjax()) {
            if ($this->request->data) {
                $inputId = Hash::get($this->request->data, 'input_id');
            }

            $inputName = $this->Fuga->getFugaName($inputId);
            if (strlen($inputName) === 0) {
                return '※存在しません';
            } else {
                return $inputName;
            }
        }
    }
```

```View/Hoge/index.ctp
        <!-- フォーム -->
        <?php echo $this->Form->create('Hoge'); ?>
        <?= $this->Form->input('modified_by', ['type' => 'hidden']) ?>
        <table class="formTable defaultTable">
            <colgroup>
                <col class="em8" />
            </colgroup>
            <tr>
                <th>対象ID</th>
                <td>
                    <!-- ID入力 -->
                    <?php
                        echo $this->Form->input('input_id', [
                            'type'     => 'text',
                            'div'      => false,
                            'label'    => false,
                            'required' => false,
                        ]);
                    ?>
                    <!-- 入力IDに応じて名前表示 -->
                    <span id="input_name"></span>
                </td>
            </tr>
        </table>
        <div class="submitArea">
            <ul class="submitButtons">
                <li>
                    <label class="submitBase primary" for="submit">
                        <?php
                            echo $this->Form->submit('保存', ['id' => 'submit']);
                        ?>
                        保存
                    </label>
                </li>
                <li>
                    <?php echo $this->Html->link('戻る', $this->Session->read('Hoge.lastIndexUrl'), ['class' => 'submitBase inverse']); ?>
                </li>
            </ul>
        </div>
        <?php echo $this->Form->end(); ?>

<!-- Javascript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>
    // ID内容変更時
    $('#HogeInputId').change(function() {
        getNameWithAjax();
    });
    // 名前を非同期に取得して表示
    function getNameWithAjax() {
        const inputId = $('#HogeInputId').val();
        $.ajax({
            url: '/Hoge/fetchDBData',
            type:'POST',
            data: {
                'input_id': inputId,
            },
        })
        .then(
            data => $('#input_name').text(data),
            error => alert('名前の読み込みエラー')
        );
    }
</script>
```

## 結果

できました！！
success:とかerror:とかはもう非推奨なので使ってはいけませぬ

## エラー集

### HTTP 400

chrome console

```
jquery.js:9659 POST https://{YOUR_DOMAIN}/Hoge/fetchDBDat 400 (Bad Request)
```

errorlog

```
2019-12-16 16:13:39 Error: [AuthSecurityException] The request has been black-holed
Request URL: /Hoge/fetchDBData
Stack Trace:
#0 /vagrant/Vendor/cakephp/cakephp/lib/Cake/Controller/Component/SecurityComponent.php(490): SecurityComponent->_validToken(Object(HogeController))
```

修正箇所

```HogeController.php
        // beforeFilterにセキュリティを解除が抜けている
        $this->Security->unlockedActions = ['fetchDBData'];
```

### HTTP 404

chrome console

```
jquery.js:9659 POST https://{YOUR_DOMAIN}/Hoge/fetchDBData 404 (Not Found)
```

errorlog

```
2019-12-16 15:44:03 Error: [PrivateActionException] Private Action HogeController::fetchDBData() is not directly accessible.
Exception Attributes: array (
  'controller' => 'HogeController',
  'action' => 'fetchDBData',
)
Request URL: /Hoge/fetchDBData
Stack Trace:
#0 /vagrant/Vendor/cakephp/cakephp/lib/Cake/Routing/Dispatcher.php(193): Controller->invokeAction(Object(CakeRequest))
```

修正箇所

```HogeController.php
private function fetchDBData() {
    // 処理
}

// ↓ アクセス修飾子をprivate→publicに変更

public function fetchDBData() {
    // 処理
}
```

### HTTP 500

chrome console

```
POST https://{YOUR_DOMAIN}/Hoge/fetchDBData 500 (Internal Server Error)
```

errorlog

```
2019-12-16 15:00:02 Error: [MissingViewException] View file "/vagrant/oauth/app/.../.../View/Hoge/fetch_d_b_data" is missing.
Exception Attributes: array (
  'file' => '/vagrant/oauth/app/.../.../View/Hoge/fetch_d_b_data.ctp',
)
Request URL: /Hoge/fetchDBData
Stack Trace:
#0 /vagrant/Vendor/cakephp/cakephp/lib/Cake/View/View.php(470): AppView->_getViewFileName('Hoge...')
```

修正箇所

```HogeController.php
    public function fetchDBData() {
        // View不要の記述が抜けている
        $this->autoRender = false;
    }
}
```