cakePHP 3 でCRD実装

cakaPHP3.xで勉強がてら、管理画面的なページを作りました。
とりあえずselect, delete, insertを実装したので忘れないようにメモです

#テーブル構成
mysqlで、

```
CREATE TABLE user(
     id int,
     name varchar(128),
     create_date timestamp  NOT NULL DEFAULT CURRENT_TIMESTAMP,
);
```
したテーブルにいろいろやっていきます。

#最低限のページを出すまで
遠慮なくbakeを使っていく所存

```
bin/cake bake controller user
bin/cake bake model user
```
でControllerとModelファイル作成

Viewファイル、`View/User/index.ctp`を作成

```php:index.ctp
<h1>User</h1>
<table>
    <tr>
        <th>Id</th>
        <th>name</th>
    </tr>
</table>
```

ひとまずこれでページはできました。
`http://localhost:8765/user`(僕の環境で)にアクセスすれば
![スクリーンショット 2019-01-10 12.58.29.png](https://qiita-image-store.s3.amazonaws.com/0/178311/d494c3f9-89f3-b99a-a272-92105161021e.png)
こんな感じの表示がされてます。

#select
controllerで

```php3:UserController.php
    /**
     * Index method
     *
     * @return \Cake\Http\Response|void
     */
    public function index()
    {
        $this->set('user', $this->User->find('all'));
    }
```
を追加

ctpで

```php:index.ctp
    <?php foreach ($user as $item): ?>
    <tr>
        <td><?= $item->id ?></td>
        <td><?= $item->name ?></td>
        </td>
    </tr>
    <?php endforeach; ?>
```
として適当なデータをinsert。

##「pKeyがないよ」エラー
「pKeyがないよ」エラーでデータを取ってくることすらできない
そういえば作ってなかったので`ALTER TABLE user ADD PRIMARY KEY (id);`でidカラムをpKeyにする
エラーが消えない…あれ…？

どうもtable定義をしているphpファイルにも明示的に書かなきゃいけないらしい
table定義をしているのは

```
bin/cake bake model user
```
で作った`Model/Table/UserTable.php`らしい。
これを見ると、

```php3:UserTable.php
    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->setTable('user');
        $this->setDisplayField('name');
    }
```
となってた。確かにpKeyの定義してないっすね。
`$this->primaryKey('id');`
を追加して解決

表示の方は
![スクリーンショット 2019-01-10 14.04.18.png](https://qiita-image-store.s3.amazonaws.com/0/178311/cfb2e6b3-c388-fb96-0f24-fa6fd2e7cb8e.png)
となる

#insert
まずフォームから
index.ctpに

```php:index.ctp
    <?php
        //createでフォームを宣言
        echo $this->Form->create('User',array(
            'type' => 'post', //type属性を指定
            'url' => 'user/add' //POST先のURL
        ));

        //下記パラメータの数だけ列挙
        echo $this->Form->input('id',array(
            'label' => 'ユーザーID'
        ));

        echo $this->Form->input('name',array(
            'label' => 'ユーザー名'
        ));

        //submitボタン作成。引数を入れるとその文言をボタンに出してくれる
        echo $this->Form->submit("登録");
    ?>
```
を追加
各メソッドのオプションはいっぱいあるのであとで別記します
上記を追加すると
![スクリーンショット 2019-01-10 14.07.37.png](https://qiita-image-store.s3.amazonaws.com/0/178311/2b376973-2a14-99c8-adc9-2a6eb9f6e4c7.png)
みたいな画面になります。
controllerに

```php3:UserController.php
     /**
     * Add method
     *
     * @return \Cake\Http\Response|null Redirects on successful add, renders view otherwise.
     */
    public function add()
    {
        $user = $this->User->newEntity();
        if ($this->request->is('post')) {
            $user = $this->User->patchEntity($user, $this->request->getData());

            if ($this->User->save($user)) {
                $this->Flash->success(__('The user has been added.'));
                return $this->redirect(['action' => 'index']);
            }
            else{
                //エラーログをmy_app_name/logs/debug.logに出力
                echo $this->log(print_r($user->errors(),true),LOG_DEBUG);
            }
            $this->Flash->error(__('The user could not be saved. Please, try again.'));
        }
        $this->redirect('/user');
    }
```
を追加。
`$user = $this->User->patchEntity($user, $this->request->getData());`
で`$user`にフォームの値を代入、
`$this->User->save($user)`
でそのモデルを登録してます。

##「create_dateがnullだよ」エラー
ここで問題発生。
上記のままinsertしようとすると、「create_dateがnullだよ」エラーが発生。
DEFAULT CURRENT_TIMESTAMP入れてるのに…？
どうもテーブルにインサートするときのバリデーションに引っかかってたらしい
`Model/Table/UserTable.php`を見ると、

```php3:UserTable.php
    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {
        $validator
            ->integer('id')
            ->allowEmpty('id');

        $validator
            ->scalar('name')
            ->maxLength('name', 128)
            ->allowEmpty('name');

        $validator
            ->dateTime('create_date')
            ->requirePresence('create_date', 'create')
            ->notEmpty('create_date');

        return $validator;
    }
```
となってました。
`requirePresence`は値の更新の際、nullを受け付けるかどうか。
これが`create`だとinsertのときのみnullは受け付けませんよ。ということになるらしい
なので

```php3
        $validator
            ->dateTime('create_date')
            ->requirePresence('create_date', false)
            ->notEmpty('create_date');
```
にして解決
bakeだとdefaultオプション見てくれないのかなと一瞬思いましたが、よく考えたらそれ入れてるからってnullを許すかどうかはまた別問題ですよね。

#delete
とりあえず、ctpにフォームを追加

```php:index.ctp
    <?php
        //削除フォーム
        //createでフォームを宣言
        echo $this->Form->create('deleteUser',array(
            'type' => 'post', //type属性を指定
            'url' => 'user/delete' //POST先のURL
        ));

        //下記パラメータの数だけ列挙
        echo $this->Form->input('id',array(
            'label' => '削除したいユーザーID'
        ));

        //submitボタン作成。引数を入れるとその文言をボタンに出してくれる
        echo $this->Form->submit("削除");
    ?>
```

controllerで使うのは

```php3:UserController.php
/**
     * Delete method
     *
     * @param string|null $id User id.
     * @return \Cake\Http\Response|null Redirects to index.
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
     */
    public function delete($id = null)
    {
        $this->request->allowMethod(['post', 'delete']);
        $user = $this->User->get($id);

        if ($this->User->delete($user)) {
            $this->Flash->success(__('The user has been deleted.'));
        } else {
            $this->Flash->error(__('The user could not be deleted. Please, try again.'));
        }

        return $this->redirect(['action' => 'index']);
    }
```
のメソッド

##意図しないアクションにPOSTされる
「削除」の方のボタンを押してるのに頑なにinsertしようとしてました。
原因はinsertする方のフォームに
`echo $this -> Form -> end ();`
がなかったこと。
この構文でフォームを閉じてるので、これがなかったら当然下のボタンも`user/add`アクションにひもづきます。
ということで

```php3:index.ctp
    <?php
        //createでフォームを宣言
        echo $this->Form->create('User',array(
            'type' => 'post', //type属性を指定
            'url' => 'user/add' //POST先のURL
        ));

        //下記パラメータの数だけ列挙
        echo $this->Form->input('id',array(
            'div' => false,
            'label' => 'ユーザーID'
        ));

        echo $this->Form->input('name',array(
            'div' => false,
            'label' => 'ユーザー名'
        ));

        //submitボタン作成。引数を入れるとその文言をボタンに出してくれる
        echo $this->Form->submit("登録");
        echo $this -> Form -> end ();//追加
    ?>

    <?php
        //削除フォーム
        //createでフォームを宣言
        echo $this->Form->create('deleteUser',array(
            'type' => 'post', //type属性を指定
            'url' => 'user/delete' //POST先のURL
        ));

        //下記パラメータの数だけ列挙
        echo $this->Form->input('id',array(
            'label' => '削除したいユーザーID'
        ));

        //submitボタン作成。引数を入れるとその文言をボタンに出してくれる
        echo $this->Form->submit("削除");
        echo $this -> Form -> end ();//追加
    ?>
```
としたところちゃんとdeleteアクションの方に飛びました。
これでやっと削除ができる…と思いきや別の問題発生

##引数にnullが入ってくる
`$user = $this->User->get($id);`
で`$id`にnullが入ってしまい、「そんなデータないよ」エラーが発生しました。
引数に`$id`指定してるのに…？と思いましたが、アクションの引数に入ってくるのはあくまでもルーティングで指定してる値であって、フォームのリクエストではないです。
そして指定してたURLは`'user/delete'`だったので、当然nullが入ってきます。

対応として、`$id = $this->request->getData("id");`を追加
`$this->request`にはフォームで送られてきたデータが入ってます。
`getData()`は引数を指定しなければ全部、指定すれば指定したパラメータだけ取って来れます。
これでやっとdeleteできました。