36

私は、1つのプログラミングの問題を解決する方法はたくさんあることを学びました。それぞれのアプローチには、通常、独自の利点とマイナスの影響があります。

私が今日決定しようとしているのは、PHPでモデル検証を行うための最良の方法です。人の例を使って、私が過去に使用した4つの異なるアプローチの概要を説明しました。それぞれに、クラスと使用例、および各アプローチの好きなところと嫌いなところが含まれています。

ここでの私の質問はこれです:あなたはどちらのアプローチが最良だと思いますか?それとも、より良いアプローチがありますか?

アプローチ#1:モデルクラスのセッターメソッドを使用した検証

いいもの

  • シンプル、たった1つのクラス
  • 例外をスローすることにより、クラスが無効な状態になることはありません(ビジネスロジックを除いて、つまり、死は出生前に発生します)
  • 検証メソッドを呼び出すことを忘れないでください

悪い人

  • Exception(経由で)1つのエラーのみを返すことができます
  • エラーがそれほど例外的ではない場合でも、例外を使用してキャッチする必要があります
  • 他のパラメータがまだ設定されていない可能性があるため、1つのパラメータにのみ作用できます(比較する方法がありませbirth_datedeath_date
  • 検証が多いため、モデルクラスが長くなる可能性があります
class Person
{
    public $name;
    public $birth_date;
    public $death_date;

    public function set_name($name)
    {
        if (!is_string($name))
        {
            throw new Exception('Not a string.');
        }

        $this->name = $name;
    }

    public function set_birth_date($birth_date)
    {
        if (!is_string($birth_date))
        {
            throw new Exception('Not a string.');
        }

        if (!preg_match('/(\d{4})-([01]\d)-([0-3]\d)/', $birth_date))
        {
            throw new Exception('Not a valid date.');
        }

        $this->birth_date = $birth_date;
    }

    public function set_death_date($death_date)
    {
        if (!is_string($death_date))
        {
            throw new Exception('Not a string.');
        }

        if (!preg_match('/(\d{4})-([01]\d)-([0-3]\d)/', $death_date))
        {
            throw new Exception('Not a valid date.');
        }

        $this->death_date = $death_date;
    }
}
// Usage:

try
{
    $person = new Person();
    $person->set_name('John');
    $person->set_birth_date('1930-01-01');
    $person->set_death_date('2010-06-06');
}
catch (Exception $exception)
{
    // Handle error with $exception
}

アプローチ#2:モデルクラスの検証メソッドを使用した検証

いいもの

  • シンプル、たった1つのクラス
  • 複数のパラメーターを検証(比較)することが可能(すべてのモデルパラメーターが設定された後に検証が行われるため)
  • 複数のエラーを返すことができます(errors()メソッドを介して)
  • 例外からの解放
  • getterメソッドとsetterメソッドを他のタスクで使用できるようにします

悪い人

  • モデルが無効な状態になっている可能性があります
  • is_valid()開発者は検証メソッドを呼び出すことを忘れないでください
  • 検証が多いため、モデルクラスが長くなる可能性があります
class Person
{
    public $name;
    public $birth_date;
    public $death_date;

    private $errors;

    public function errors()
    {
        return $this->errors;
    }

    public function is_valid()
    {
        $this->validate_name();
        $this->validate_birth_date();
        $this->validate_death_date();

        return count($this->errors) === 0;
    }

    private function validate_name()
    {
        if (!is_string($this->name))
        {
            $this->errors['name'] = 'Not a string.';
        }
    }

    private function validate_birth_date()
    {
        if (!is_string($this->birth_date))
        {
            $this->errors['birth_date'] = 'Not a string.';
            break;
        }

        if (!preg_match('/(\d{4})-([01]\d)-([0-3]\d)/', $this->birth_date))
        {
            $this->errors['birth_date'] = 'Not a valid date.';
        }
    }

    private function validate_death_date()
    {
        if (!is_string($this->death_date))
        {
            $this->errors['death_date'] = 'Not a string.';
            break;
        }

        if (!preg_match('/(\d{4})-([01]\d)-([0-3]\d)/', $this->death_date))
        {
            $this->errors['death_date'] = 'Not a valid date.';
            break;
        }

        if ($this->death_date < $this->birth_date)
        {
            $this->errors['death_date'] = 'Death cannot occur before birth';
        }
    }
}
// Usage:

$person = new Person();
$person->name = 'John';
$person->birth_date = '1930-01-01';
$person->death_date = '2010-06-06';

if (!$person->is_valid())
{
    // Handle errors with $person->errors()
}

アプローチ#3:個別の検証クラスでの検証

いいもの

  • 非常に単純なモデル(すべての検証は別のクラスで行われます)
  • 複数のパラメーターを検証(比較)することが可能(すべてのモデルパラメーターが設定された後に検証が行われるため)
  • 複数のエラーを返すことができます(errors()メソッドを介して)
  • 例外からの解放
  • getterメソッドとsetterメソッドを他のタスクで使用できるようにします

悪い人

  • モデルごとに2つのクラスが必要なため、少し複雑です。
  • モデルが無効な状態になっている可能性があります
  • 開発者は、検証クラスを使用することを忘れないでください
class Person
{
    public $name;
    public $birth_date;
    public $death_date;
}
class Person_Validator
{
    private $person;
    private $errors = array();

    public function __construct(Person $person)
    {
        $this->person = $person;
    }

    public function errors()
    {
        return $this->errors;
    }

    public function is_valid()
    {
        $this->validate_name();
        $this->validate_birth_date();
        $this->validate_death_date();

        return count($this->errors) === 0;
    }

    private function validate_name()
    {
        if (!is_string($this->person->name))
        {
            $this->errors['name'] = 'Not a string.';
        }
    }

    private function validate_birth_date()
    {
        if (!is_string($this->person->birth_date))
        {
            $this->errors['birth_date'] = 'Not a string.';
            break;
        }

        if (!preg_match('/(\d{4})-([01]\d)-([0-3]\d)/', $this->person->birth_date))
        {
            $this->errors['birth_date'] = 'Not a valid date.';
        }
    }

    private function validate_death_date()
    {
        if (!is_string($this->person->death_date))
        {
            $this->errors['death_date'] = 'Not a string.';
            break;
        }

        if (!preg_match('/(\d{4})-([01]\d)-([0-3]\d)/', $this->person->death_date))
        {
            $this->errors['death_date'] = 'Not a valid date.';
            break;
        }

        if ($this->person->death_date < $this->person->birth_date)
        {
            $this->errors['death_date'] = 'Death cannot occur before birth';
        }
    }
}
// Usage:

$person = new Person();
$person->name = 'John';
$person->birth_date = '1930-01-01';
$person->death_date = '2010-06-06';

$validator = new Person_Validator($person);

if (!$validator->is_valid())
{
    // Handle errors with $validator->errors()
}

アプローチ#4:モデルクラスと検証クラスでの検証

いいもの

  • 例外をスローすることにより、クラスが無効な状態になることはありません(ビジネスロジックを除いて、つまり、死は出生前に発生します)
  • 複数のパラメーターを検証(比較)することが可能(すべてのモデルパラメーターが設定された後にビジネス検証が行われるため)
  • 複数のエラーを返すことができます(errors()メソッドを介して)
  • 検証は、タイプ(モデルクラス)とビジネス(検証クラス)の2つのグループに編成されています。
  • getterメソッドとsetterメソッドを他のタスクで使用できるようにします

悪い人

  • スローされる例外(モデルクラス)とエラー配列(検証クラス)がある場合、エラー処理はより複雑になります。
  • モデルごとに2つのクラスが必要なため、少し複雑です。
  • 開発者は、検証クラスを使用することを忘れないでください
class Person
{
    public $name;
    public $birth_date;
    public $death_date;

    private function validate_name()
    {
        if (!is_string($this->person->name))
        {
            $this->errors['name'] = 'Not a string.';
        }
    }

    private function validate_birth_date()
    {
        if (!is_string($this->person->birth_date))
        {
            $this->errors['birth_date'] = 'Not a string.';
            break;
        }

        if (!preg_match('/(\d{4})-([01]\d)-([0-3]\d)/', $this->person->birth_date))
        {
            $this->errors['birth_date'] = 'Not a valid date.';          
        }
    }

    private function validate_death_date()
    {
        if (!is_string($this->person->death_date))
        {
            $this->errors['death_date'] = 'Not a string.';
            break;
        }

        if (!preg_match('/(\d{4})-([01]\d)-([0-3]\d)/', $this->person->death_date))
        {
            $this->errors['death_date'] = 'Not a valid date.';
        }
    }
}
class Person_Validator
{
    private $person;
    private $errors = array();

    public function __construct(Person $person)
    {
        $this->person = $person;
    }

    public function errors()
    {
        return $this->errors;
    }

    public function is_valid()
    {
        $this->validate_death_date();

        return count($this->errors) === 0;
    }

    private function validate_death_date()
    {
        if ($this->person->death_date < $this->person->birth_date)
        {
            $this->errors['death_date'] = 'Death cannot occur before birth';
        }
    }
}
// Usage:

try
{
    $person = new Person();
    $person->set_name('John');
    $person->set_birth_date('1930-01-01');
    $person->set_death_date('2010-06-06');

    $validator = new Person_Validator($person);

    if (!$validator->is_valid())
    {
        // Handle errors with $validator->errors()
    }
}
catch (Exception $exception)
{
    // Handle error with $exception
}
4

1 に答える 1

2

最善のアプローチは1つだけではないと思います。それは、クラスをどのように使用するかによって異なります。この場合、単純なデータオブジェクトしかない場合は、アプローチ#2:モデルクラスの検証メソッドを使用した検証を使用することをお勧めします。

私の意見では、悪いことはそれほど悪くはありません。

モデルが無効な状態になっている可能性があります

モデルを無効な状態にできることが望ましい場合があります。

たとえば、WebフォームからPersonオブジェクトにデータを入力し、それをログに記録する場合です。最初のアプローチを使用する場合は、Personクラスを拡張し、すべてのセッターをオーバーライドして例外をキャッチする必要があります。そうすると、このオブジェクトをログに対して無効な状態にすることができます。

開発者は、検証is_valid()メソッドを呼び出すことを忘れないでください

モデルが絶対に無効な状態であってはならない場合、またはメソッドでモデルが有効な状態である必要がある場合はis_valid()、クラス内からいつでも呼び出して、モデルが有効な状態であることを確認できます。

検証が多いため、モデルクラスが長くなる可能性があります

検証コードはまだどこかに行く必要があります。ほとんどのエディターでは、関数を折りたたむことができるため、コードの読み取り中に問題が発生することはありません。どちらかといえば、すべての検証を1か所で行うのは良いことだと思います。

于 2013-03-04T21:02:23.443 に答える