Leggendo questa domanda SO sembra che vengano lanciate eccezioni per la convalida dell'input dell'utente è disapprovato.
Ma chi dovrebbe convalidare questi dati? Nelle mie applicazioni, tutte le convalide vengono eseguite nel livello aziendale, perché solo la classe stessa sa veramente quali valori sono validi per ciascuna delle sue proprietà. Se dovessi copiare le regole per convalidare una proprietà sul controller, è possibile che le regole di convalida cambino e ora ci sono due punti in cui la modifica dovrebbe essere fatta.
La mia premessa è che la convalida dovrebbe essere eseguita sul livello aziendale in modo errato?
Cosa faccio
Quindi il mio codice di solito finisce in questo modo:
<?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
}
Nel controller, ho appena passato i dati di input al modello e catturato le eccezioni generate per mostrare l'errore (i) all'utente:
<?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);
}
Questa è una cattiva metodologia?
Metodo alternativo
Dovrei forse creare metodi per isValidAge($a)
che restituiscono true / false e quindi chiamarli dal controller?
<?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 il controller sarà praticamente lo stesso, invece di try / catch ci sono ora se / 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);
}
Quindi, cosa dovrei fare?
Sono abbastanza contento del mio metodo originale, e ai miei colleghi a cui l'ho mostrato in generale è piaciuto. Nonostante ciò, dovrei passare al metodo alternativo? O sto facendo questo terribilmente male e dovrei cercare un altro modo?