Читаючи це запитання ТА, здається, що викидання винятків для перевірки введення користувача нахмурюється.
Але хто повинен підтвердити ці дані? У моїх програмах усі перевірки проводяться на рівні бізнесу, оскільки тільки сам клас дійсно знає, які значення є дійсними для кожного з його властивостей. Якщо я повинен був скопіювати правила перевірки властивості до контролера, можливо, правила перевірки змінюються, і тепер є два місця, де слід внести зміни.
Чи моє переконання, що перевірку слід робити на бізнес-шарі, неправильно?
Що я роблю
Тому мій код зазвичай закінчується так:
<?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
}
І контролер буде в основному тим самим, тільки замість спробувати / catch зараз є, якщо / 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
та інші винятки