Czytając to pytanie SO , wydaje się, że odrzucanie wyjątków w celu sprawdzania poprawności danych wejściowych użytkownika jest niezadowolone.
Ale kto powinien zweryfikować te dane? W moich aplikacjach wszystkie walidacje są wykonywane w warstwie biznesowej, ponieważ tylko sama klasa naprawdę wie, które wartości są prawidłowe dla każdej z jej właściwości. Gdybym miał skopiować zasady sprawdzania poprawności właściwości do kontrolera, możliwe jest, że reguły sprawdzania poprawności ulegną zmianie i teraz są dwa miejsca, w których należy dokonać modyfikacji.
Czy moje założenie, że walidacja powinna odbywać się w warstwie biznesowej, jest błędne?
Co robię
Więc mój kod zwykle kończy się tak:
<?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
}
W kontrolerze po prostu przekazuję dane wejściowe do modelu i wychwytuję zgłoszone wyjątki, aby pokazać użytkownikowi błąd:
<?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);
}
Czy to zła metodologia?
Metoda alternatywna
Czy powinienem utworzyć metody isValidAge($a)
zwracania wartości prawda / fałsz, a następnie wywołać je ze sterownika?
<?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
}
Kontroler będzie w zasadzie taki sam, tylko zamiast try / catch są teraz 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);
}
Więc co powinienem zrobić?
Jestem bardzo zadowolony z mojej oryginalnej metody i podobają mi się moi koledzy, którym to pokazałem. Mimo to, czy powinienem przejść na alternatywną metodę? A może robię to okropnie źle i powinienem szukać innej drogi?
IValidateResults
.
ValidationException
innymi wyjątkami