5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Phalconでカスタムバリデーションを作る

Posted at

前置き

Phalcon使い始めの頃、モデルのバリデーションのコードをみると、よく以下のようなコードを見かけた。

public function isValid()
{
        // account ID
        if($this->account_id === null || $this->account_id === '')
        {
            $this->appendMessage(new Message('account-id.required', 'account_id', 'accountIdRequired'));
        }
        else if(!is_numeric($this->account_id) || $this->account_id < 1)
        {
            $this->appendMessage(new Message('account-id.invalid', 'account_id', 'accountIdInvalid'));
        }
        else if()
        ....

        return $this->validationHasFailed() !== true;
}

せっかくPhalconがバリデーションを提供してくれているのに、なぜそれを使わないのか尋ねてみたところ、Phalconが提供しているバリデーションだけだとチェックできないことがあるうえ、Phalconのドキュメントがわかりにくくカスタムバリデーションの作り方もよくわからないし、、バリデーションをifで書いたほうが早いと言われた。

いいたいことはわかるのだが、こういったことを繰り返してコードのメンテナンス性が悪くなっていくのが目に見えているため、改善しようと思い立ち、その時に作成したカスタムバリデータークラスや、クラスを使わなくてもバリデーションをかけるようにするCustomクラスについて書こうと思う。

環境

PHP 5.6.26
Phalcon2.0.13

やりたいこと

Phalconのバリデーションを使ってカスタムバリデーションを簡単にかけるようにする。
こんな感じでかけるようにしたい。

$this->validation = new Validation();
$this->validation->add('account_id', new PresenceOf(array('cancelOnFail' => true)))
                 ->add('account_id', new Length(0, 255, true))
                 ->add('account_id', new Custom(
                      function($validation, $attribute, $value){
                         if ($value > 0){
                            return true;
                         }
                         return false;
                      })
                   ); //0より大きい数字であること

カスタムバリデーターのサンプル

カスタムバリデーターのサンプルクラス。このクラスはパラメータの長さをチェックする。
※実際、長さをチェックしたい場合は、PhalconがPhalcon\Validation\Validator\StringLengthを提供しているので、こちらを使うのが良い。

<?php

namespace Araiguma\Common\Validator;

use \Phalcon\Validation\Message;
use \Phalcon\Validation\Validator;
use \Phalcon\Validation\ValidatorInterface;

class Length extends Validator implements ValidatorInterface
{
    const MAX_LENGTH=10000;

    private $min;
    private $max;
    private $multiByte;

    public function __construct($min=0, $max=self::MAX_LENGTH, $multiByte=false)
    {
        $this->min = $min;
        $this->max = $max;
        $this->multiByte = $multiByte;  //multiByteがtrueの場合はmb_strlenを長さ計算に用いる

    }

    /**
     * Executes the validation
     *
     * @param Phalcon\Validation $validation
     * @param string $attribute
     * @return boolean
     */
    public function validate(\Phalcon\Validation $validation, $attribute)
    {
        $value = $validation->getValue($attribute);
        $length = $this->getLength($value);

        if ($value === true || $value === false) {
            //true, falseは文字列としてみる。
            $length = 4;
        }

        if ($this->min <= $length && $length <= $this->max)
        {
            return true;
        }

        $validation->appendMessage(new Message('invalidLength', $attribute, 'invalidLength'));
        return false;
    }

    private function getLength($value)
    {
        if ($this->multiByte)
        {
            return mb_strlen($value);
        }

        return strlen($value);
    }
}

Customクラス

バリデーターのクラスをつくらなくても、コードをかけるように作ったCustomクラス。

<?php
/**
 * 任意のバリデーションを登録できるバリデータ。
 */

namespace Araiguma\Common\Validator;

use \Phalcon\Validation\Message;
use \Phalcon\Validation\Validator;
use \Phalcon\Validation\ValidatorInterface;

class Custom extends Validator implements ValidatorInterface
{
    private $func;

    /**
     * funcには以下の引き数を定義した関数を指定する。
     *
     * func($validation, $val, $attribute)
     * $validation: Phalcon\Validation
     * $val:        mix    'attribute'の値
     * $attribute:  string 'チェックするフィールド名'
     **/
    public function __construct($func)
    {
        $this->func = $func;
    }

    /**
     * Executes the validation
     *
     * @param Phalcon\Validation $validation
     * @param string $attribute
     * @return boolean
     */
    public function validate(\Phalcon\Validation $validation, $attribute)
    {
        $value = $validation->getValue($attribute);

        return call_user_func($this->func, $validation, $attribute, $value);
    }
}

最終的なコード例

上のクラスを利用して、以下のような感じでかけるようになった。

namespace Araiguma\Mvc\Model;

use Araiguma\Common\Validator\Custom;
use Araiguma\Common\Validator\Length;
use Phalcon\Mvc\Model\Message as Message;
use Phalcon\Validation\Message as ValidationMessage;
use Phalcon\Validation;
use Phalcon\Validation\Validator\PresenceOf;

class AraigumaModel extends BaseModel
{
    protected $account_id         = '';
    protected $file_name          = '';

   ...

   public function isValid()
   {
        $this->validation = new Validation();
        $params = array(
            'account_id'         => $this->account_id,
            'file_name'          => $this->file_name
        );

        $this->validation->add('account_id', new PresenceOf(array('cancelOnFail' => true)))
            ->add('account_id', new Custom(
                function($validation, $attribute, $value){
                    if ($value > 0){
                        return true;
                    }
                    return false;
                })
            ); //0より大きい数字であること

        $this->validation->add('file_name', new PresenceOf(array('cancelOnFail' => true)))
            ->add('file_name', new Length(0, 255, true));
            ->add('file_name', new Custom(
                function($validation, $attribute, $value){
                    if (self::findFirstByFileName($value)) {
                        $validation->appendMessage(new ValidationMessage('同じファイル名がすでに存在します。', $attribute, 'duplicateFileName'));
                        return false;
                    }

                    return true;
                })
            ); // 同じファイル名がDBに存在しているか

        $messages = $this->validation->validate($params);
        foreach ($messages as $message) {
            $this->appendMessage(new Message( $message->getMessage() , $message->getField(), $message->getType()));
        }

        return !$this->validationHasFailed();
    }
}

結果

プロジェクト的には、のちの資産になるため各バリデーションで独自クラスは毎回作るようにしたいが、どうしてもそれが大変というときはCustomクラスを今でも使ってます。

課題

既存の各バリデーションも全部書き換えたいが、そちらにはなかなか手をつけられない...

テスト

以下に各クラスのphpunitのテストを載せておく。
LengthTest.php
CustomTest.php

参考

[Phalcon Validation]
(http://phalcon-docs-ja.readthedocs.io/ja/stable/reference/validation.html)

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?