Lendo essa pergunta do SO , parece que lançar exceções para validar a entrada do usuário é desaprovado.
Mas quem deve validar esses dados? Nos meus aplicativos, todas as validações são feitas na camada de negócios, porque apenas a própria classe realmente sabe quais valores são válidos para cada uma de suas propriedades. Se eu copiasse as regras para validar uma propriedade para o controlador, é possível que as regras de validação mudem e agora existem dois locais onde a modificação deve ser feita.
Minha premissa é que a validação deve ser feita na camada de negócios errada?
O que eu faço
Então, meu código geralmente acaba assim:
<?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
}
No controlador, eu apenas passo os dados de entrada para o modelo e pego exceções lançadas para mostrar o (s) erro (s) para o usuário:
<?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);
}
Essa é uma metodologia ruim?
Método alternativo
Talvez eu deva criar métodos para isValidAge($a)
esse retorno verdadeiro / falso e depois chamá-los do controlador?
<?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
}
E o controlador será basicamente o mesmo, apenas em vez de try / catch, existem agora 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);
}
Então, o que eu deveria fazer?
Estou muito feliz com meu método original e meus colegas a quem eu o mostrei em geral gostaram. Apesar disso, devo mudar para o método alternativo? Ou estou fazendo isso terrivelmente errado e devo procurar outro caminho?
IValidateResults
.
ValidationException
e outras exceções