Читая этот SO вопрос, кажется, что выбрасывание исключений для проверки пользовательского ввода не одобряется.
Но кто должен проверять эти данные? В моих приложениях все проверки выполняются на бизнес-уровне, потому что только сам класс действительно знает, какие значения допустимы для каждого из его свойств. Если бы я скопировал правила проверки свойства в контроллер, вполне возможно, что правила проверки изменятся, и теперь есть два места, где нужно внести изменения.
Является ли моя предпосылка о том, что проверка должна проводиться на бизнес-уровне неправильно?
Что я делаю
Так что мой код обычно заканчивается так:
<?php
class Person
{
private $name;
private $age;
public function setName($n) {
$n = trim($n);
if (mb_strlen($n) == 0) {
throw new ValidationException("Name cannot be empty");
}
$this->name = $n;
}
public function setAge($a) {
if (!is_int($a)) {
if (!ctype_digit(trim($a))) {
throw new ValidationException("Age $a is not valid");
}
$a = (int)$a;
}
if ($a < 0 || $a > 150) {
throw new ValidationException("Age $a is out of bounds");
}
$this->age = $a;
}
// other getters, setters and methods
}
В контроллере я просто передаю входные данные в модель и ловлю выданные исключения, чтобы показать пользователю ошибки:
<?php
$person = new Person();
$errors = array();
// global try for all exceptions other than ValidationException
try {
// validation and process (if everything ok)
try {
$person->setAge($_POST['age']);
} catch (ValidationException $e) {
$errors['age'] = $e->getMessage();
}
try {
$person->setName($_POST['name']);
} catch (ValidationException $e) {
$errors['name'] = $e->getMessage();
}
...
} catch (Exception $e) {
// log the error, send 500 internal server error to the client
// and finish the request
}
if (count($errors) == 0) {
// process
} else {
showErrorsToUser($errors);
}
Это плохая методология?
Альтернативный метод
Должен ли я создать методы для isValidAge($a)
возврата true / false и затем вызвать их из контроллера?
<?php
class Person
{
private $name;
private $age;
public function setName($n) {
$n = trim($n);
if ($this->isValidName($n)) {
$this->name = $n;
} else {
throw new Exception("Invalid name");
}
}
public function setAge($a) {
if ($this->isValidAge($a)) {
$this->age = $a;
} else {
throw new Exception("Invalid age");
}
}
public function isValidName($n) {
$n = trim($n);
if (mb_strlen($n) == 0) {
return false;
}
return true;
}
public function isValidAge($a) {
if (!is_int($a)) {
if (!ctype_digit(trim($a))) {
return false;
}
$a = (int)$a;
}
if ($a < 0 || $a > 150) {
return false;
}
return true;
}
// other getters, setters and methods
}
И контроллер будет в основном таким же, просто вместо try / catch теперь есть if / else:
<?php
$person = new Person();
$errors = array();
if ($person->isValidAge($age)) {
$person->setAge($age);
} catch (Exception $e) {
$errors['age'] = "Invalid age";
}
if ($person->isValidName($name)) {
$person->setName($name);
} catch (Exception $e) {
$errors['name'] = "Invalid name";
}
...
if (count($errors) == 0) {
// process
} else {
showErrorsToUser($errors);
}
И что я должен делать?
Я очень доволен своим оригинальным методом, и мои коллеги, которым я его показал, в целом понравились. Несмотря на это, я должен перейти на альтернативный метод? Или я делаю это ужасно неправильно, и я должен искать другой путь?
IValidateResults
.
ValidationException
и других исключений