HumHubでvalidationルールを追加する

## 概要
HumHubのmodelのvalidationルールの追加方法。
以下の要件を例に説明する。

## 要件
userモジュールの、Userモデルの、usernameは半角英数字とハイフン、アンダーバーのみに制限する。

- 対象のモデル
   - protected/humhub/modules/user/models/User.php

## 関連
- [HumHubというCMSを使ってSNSを秒で作る](https://qiita.com/Esfahan/items/974687234566ef6b069d)

## 実装方法
以下の2つの実装方法がある。
両方について説明をする。

1. Inline validators
    - モデルのメソッドまたは無名関数として定義する方法
2. Standalone validators
    - yii\validators\Validator、またはその子クラスをを拡張する方法

## 共通
Inline validators, Standalone validatorsともに共通の設定

### エラーメッセージの定義
ルールに違反した際に表示するエラーメッセージを以下で定義する。

以下のファイルを新規作成する。
messages/ja/validation_models_User.php

```php
return [
    'HalfWidth' => '半角英数字、ハイフン、アンダーバーのみで入力してください',
];
```

## Inline validators
モデルのメソッドまたは無名関数として定義する方法

### モデルのメソッドで定義する方法
値が半角英数字、ハイフン、アンダーバーのみで構成されており、6文字以上18文字以下かどうかをチェックするメソッドを追加する。publicでないといけない。

また、`rules()`メソッドに作成したルールを追加する。

protected/humhub/modules/user/models/User.php

```diff
    /**
     * @inheritdoc
     */
    public function rules()
    {
        /* @var $userModule \humhub\modules\user\Module */
        $userModule = Yii::$app->getModule('user');

        return [
            [['username'], 'required'],
+           [['username'], 'half_width'],
            [['status', 'created_by', 'updated_by', 'visibility'], 'integer'],
            [['status', 'visibility'], 'integer'],
            [['tags'], 'string'],
            [['guid'], 'string', 'max' => 45],
            [['username'], 'string', 'max' => 50, 'min' => $userModule->minimumUsernameLength],
            [['time_zone'], 'in', 'range' => \DateTimeZone::listIdentifiers()],
            [['auth_mode'], 'string', 'max' => 10],
            [['language'], 'string', 'max' => 5],
            [['email'], 'unique'],
            [['email'], 'email'],
            [['email'], 'string', 'max' => 100],
            [['email'], 'required', 'when' => function($model, $attribute) use ($userModule) {
                    return $userModule->emailRequired;
                }],
            [['username'], 'unique'],
            [['guid'], 'unique'],
        ];
    }

/* snip */

+   // 最下部に追記
+   /**
+    * Check value consits of only half-width characters and - and _
+    */
+   public function half_width($attribute, $params)
+   {
+       if(strlen($this->$attribute) > 0) {
+           if (!preg_match("/^[a-zA-Z0-9_-]+$/", $this->$attribute)) {
+               $this->addError($attribute, Yii::t('UserModule.validation_models_User', 'HalfWidth'));
+           }
+       }
+   }
```

### 無名関数として定義する方法
protected/humhub/modules/user/models/User.php
の`rules()`のメソッドに以下のように定義する

```php
    public function rules()
    {
        return [
            ['username', function ($attribute, $params, $validator) {
                if (!preg_match("/^[a-zA-Z0-9_-]+$/", $this->$attribute)) {
                    $this->addError($attribute, sprintf(Yii::t('UserModule.validation_models_User', 'HalfWidth'));
                }
            }],
        ];
    }
```

## Standalone validator
yii\validators\Validator、またはその子クラスをを拡張する方法

### Validatorクラスの作成
以下のファイルを新規作成する。

rotected/humhub/components/validators/HalfWidthValidator.php

```php
<?php
namespace humhub\components\validators;

use Yii;
use yii\validators\Validator;

/**
 * Description of HalfWidthValidator
 *
 * @deprecated since version 1.0.0
 * @author xxxx
 */
class HalfWidthValidator extends Validator
{
    /**
     * Check value consits of only half-width characters and - and _
     */
    public function validateAttribute($model, $attribute)
    {
        if(strlen($model->$attribute) > 0) {
            if (!preg_match("/^[a-zA-Z0-9_-]+$/", $model->$attribute)) {
                $this->addError($model, $attribute, Yii::t('UserModule.validation_models_User', 'HalfWidth'));
            }
        }
    }
}
```

モデルのファイルで、作成したValidatorを呼び出して使用する。
protected/humhub/modules/user/models/User.php

```diff
<?php

/**
 * @link https://www.humhub.org/
 * @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
 * @license https://www.humhub.com/licences
 */

namespace humhub\modules\user\models;

use Yii;
use yii\base\Exception;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\user\components\ActiveQueryUser;
use humhub\modules\user\events\UserEvent;
use humhub\modules\friendship\models\Friendship;
use humhub\modules\space\models\Space;
use humhub\modules\content\models\Content;
use humhub\modules\space\models\Membership;
+use humhub\components\validators\HalfWidthValidator;

/* snip */

    /**
     * @inheritdoc
     */
    public function rules()
    {
        /* @var $userModule \humhub\modules\user\Module */
        $userModule = Yii::$app->getModule('user');

        return [
            [['username'], 'required'],
+           [['username'], HalfWidthValidator::className()],
            [['status', 'created_by', 'updated_by', 'visibility'], 'integer'],
            [['status', 'visibility'], 'integer'],
            [['tags'], 'string'],
            [['guid'], 'string', 'max' => 45],
            [['username'], 'string', 'max' => 50, 'min' => $userModule->minimumUsernameLength],
            [['time_zone'], 'in', 'range' => \DateTimeZone::listIdentifiers()],
            [['auth_mode'], 'string', 'max' => 10],
            [['language'], 'string', 'max' => 5],
            [['email'], 'unique'],
            [['email'], 'email'],
            [['email'], 'string', 'max' => 100],
            [['email'], 'required', 'when' => function($model, $attribute) use ($userModule) {
                    return $userModule->emailRequired;
                }],
            [['username'], 'unique'],
            [['guid'], 'unique'],
        ];
    }
```

## 検証
これで、ユーザー名を日本語で登録しようとすると、設定したエラーが表示されるようになりました。

<img width="501" alt="スクリーンショット 2018-02-13 17.14.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/105936/15704152-fb9e-fe34-1b3b-65bad1967734.png">


以上

## 参考
- [yiiframework/doc-2.0 - Validating Input](http://www.yiiframework.com/doc-2.0/guide-input-validation.html#creating-validators)