CakePHP3 Ajaxを使ったバリデーション

[こちら](http://bashalog.c-brains.jp/14/07/17-100000.php)を参考にして、CakePHP3でAJAXを使ったバリデーションに挑戦してみました。

## 前提

* フォームのフォーカスが外れた際に、validationを実行する
* フォーカスを得たときにメッセージをクリアする
* friendsofcakeのbootstrap-uiプラグインを使用している  
[CakePHP3でtwitter bootstrapを使う（導入編）](http://qiita.com/milano/items/f244f9ac5514f0e6b213)

## Javascript

以下のようなJavascriptファイルを設置します。

```js:webroot/js/ajax-validate.js
(function($){
    $.fn.validate = function(t) {
        var elements = this;
        elements.each(function(n, i) {
            $(this).on('focus', function() {
                var help_target;
                if ($(this).prev().attr("class") == 'input-group-addon') {
                    help_target = $(this).parent();
                } else {
                    help_target = $(this);
                }
                var error_target = $(this).parents(".form-group").get(0);
                $(help_target).nextAll('p.help-block').remove();
                $(error_target).removeClass('has-error');
            });
            $(this).on('blur', function() {
                var help_target;
                if ($(this).prev().attr("class") == 'input-group-addon') {
                    help_target = $(this).parent();
                } else {
                    help_target = $(this);
                }
                var error_target = $(this).parents(".form-group").get(0);
                var key = $(this).attr('name');
                var check_data = {'key': key, 'value': $(this).val()};
                $.ajax({
                    type: 'POST',
                    url: t.url,
                    data: check_data
                }).done(function(data) {
                    if (data) {
                        data = JSON.parse(data);
                        for (key in data) {
                            error_message = data[key];
                            break;
                        }
                        $(help_target).after('<p class="help-block">' + error_message + '</p>');
                        $(error_target).addClass("has-error");
                    }
                });
            });
        });
        return this;
    }
})(jQuery);
```

```js
if ($(this).prev().attr("class") == 'input-group-addon') {
    help_target = $(this).parent();
} else {
    help_target = $(this);
}
```
の部分は、viewファイル内で、prependを指定した場合と指定しない場合で、DOM構造が変わるためこのような変な記述になっています。
prepend使わないのであれば、 help_target = $(this) だけで良いはずです。

## Viewファイル

```php:src/Template/Users/add.ctp
echo $this->Form->input('username', [
    'prepend' => '<i class="fa fa-font fa-fw"></i>',
    'class' => 'ajax-validate',
]);
echo $this->Form->input('password', [
    'prepend' => '<i class="fa fa-key fa-fw"></i>',
    'class' => 'ajax-validate',
]);
```

ajax-validateというクラスをつけておきます

```php:src/Template/Users/add.ctp
echo $this->Html->script('/js/ajax-validate');
<script type="text/javascript">
$('.ajax-validate').validate({url: "/users/ajax_validate"});
</script>
```

ajax-validateクラスに設置したJavascriptファイルのメソッドをアサインします。


## Controllerファイル

```php:src/Controller/UsersController.php
public function ajax_validate()
{
    if ($this->request->is('ajax')) {
        $this->autoRender = false;
        $key = $this->request->data['key'];
        $data = [
            $key => $this->request->data['value']
        ];
        $user = $this->Users->newEntity($data);
        if ($errors = $user->errors() and array_key_exists($key, $errors)) {
            $this->response->body(json_encode($errors[$key]));
        }
    }
}
```

JSONを返すので、
$this->autoRender = false;
を指定します。