Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
3
Help us understand the problem. What is going on with this article?
@araiguma47

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

More than 3 years have passed since last update.

前置き

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

3
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
tabelog
お店選びで失敗したくない人のためのグルメサイト「食べログ」を開発しています

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
3
Help us understand the problem. What is going on with this article?