【发布时间】:2013-03-04 19:29:29
【问题描述】:
我了解到,解决一个编程问题通常有多种方法,每种方法通常都有其自身的好处和负面影响。
我今天要确定的是在 PHP 中进行模型验证的最佳方法。以一个人为例,我概述了我过去使用过的四种不同方法,每种方法都包括类和一个用法示例,以及我喜欢和不喜欢每种方法的地方。
我的问题是:你觉得哪种方法最好?还是您有更好的方法?
方法 #1:在模型类中使用 setter 方法进行验证
好的
- 简单,只有一个类
- 通过抛出异常,类永远不会处于无效状态(业务逻辑除外,即先死后生)
- 不必记得调用任何验证方法
不好的
- 只能返回 1 个错误(通过
Exception) - 需要使用异常并捕获它们,即使错误不是很异常
- 只能作用于一个参数,因为其他参数可能尚未设置(无法比较
birth_date和death_date) - 由于大量验证,模型类可能很长li>
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:使用模型类中的验证方法进行验证
好的
- 简单,只有一个类
- 可以验证(比较)多个参数(因为在设置所有模型参数后才会进行验证)
- 可以返回多个错误(通过
errors()方法) - 免于例外
- 让 getter 和 setter 方法可用于其他任务
不好的
- 模型可能处于无效状态
- 开发者一定要记得调用验证
is_valid()方法 - 由于大量验证,模型类可能很长li>
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 方法可用于其他任务
不好的
- 稍微复杂一点,因为每个模型都需要两个类
- 模型可能处于无效状态
- 开发者必须记住使用验证类
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()方法) - 验证分为两组:类型(模型类)和业务(验证类)
- 让 getter 和 setter 方法可用于其他任务
不好的
- 错误处理比较复杂,有抛出异常(模型类)和错误数组(验证类)
- 稍微复杂一点,因为每个模型都需要两个类
- 开发者必须记住使用验证类
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
}
【问题讨论】:
-
哇,你真的可以做到这一点,然后继续前进 10 倍于它所花费的时间,我猜这不适合真正的工作情况。
-
@ChrisCooney 这是一篇很好的帖子,提出了一个很好的问题。 +1
-
@AmazingDreams string typehint in php?!
-
@AmazingDreams - Type hints can not be used with scalar types such as int or string.
-
我个人喜欢方法二,可能是因为我真的很喜欢 Yii 框架,基本上他们就是这样做的。唯一需要注意的是,您很少需要调用
is_valid()或validate()或任何您称之为的名称,因为它内置在类的beforeSave()部分中。
标签: php validation model