En lisant cette question SO, il semble que lever des exceptions pour valider les entrées utilisateur est mal vu.
Mais qui devrait valider ces données? Dans mes applications, toutes les validations sont effectuées dans la couche métier, car seule la classe elle-même sait vraiment quelles valeurs sont valides pour chacune de ses propriétés. Si je copiais les règles de validation d'une propriété sur le contrôleur, il est possible que les règles de validation changent et maintenant il y a deux endroits où la modification doit être faite.
Est-ce que ma prémisse que la validation doit être effectuée sur la couche métier est incorrecte?
Ce que je fais
Donc mon code se termine généralement comme ceci:
<?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
}
Dans le contrôleur, je passe simplement les données d'entrée au modèle et intercepte les exceptions levées pour montrer les erreurs à l'utilisateur:
<?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);
}
Est-ce une mauvaise méthodologie?
Méthode alternative
Dois-je peut-être créer des méthodes pour isValidAge($a)
ce retour true / false puis les appeler à partir du contrôleur?
<?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
}
Et le contrôleur sera fondamentalement le même, juste au lieu d'essayer / attraper il y a maintenant si / sinon:
<?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);
}
Donc qu'est ce que je devrais faire?
Je suis assez content de ma méthode originale, et mes collègues à qui je l'ai montrée en général l'ont aimé. Malgré cela, dois-je passer à la méthode alternative? Ou est-ce que je fais ça terriblement mal et je devrais chercher une autre façon?
IValidateResults
.
ValidationException
et d'autres exceptions