私は、1つのプログラミングの問題を解決する方法はたくさんあることを学びました。それぞれのアプローチには、通常、独自の利点とマイナスの影響があります。
私が今日決定しようとしているのは、PHPでモデル検証を行うための最良の方法です。人の例を使って、私が過去に使用した4つの異なるアプローチの概要を説明しました。それぞれに、クラスと使用例、および各アプローチの好きなところと嫌いなところが含まれています。
ここでの私の質問はこれです:あなたはどちらのアプローチが最良だと思いますか?それとも、より良いアプローチがありますか?
アプローチ#1:モデルクラスのセッターメソッドを使用した検証
いいもの
- シンプル、たった1つのクラス
- 例外をスローすることにより、クラスが無効な状態になることはありません(ビジネスロジックを除いて、つまり、死は出生前に発生します)
- 検証メソッドを呼び出すことを忘れないでください
悪い人
Exception
(経由で)1つのエラーのみを返すことができます- エラーがそれほど例外的ではない場合でも、例外を使用してキャッチする必要があります
- 他のパラメータがまだ設定されていない可能性があるため、1つのパラメータにのみ作用できます(比較する方法がありませ
birth_date
んdeath_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
}