Let's say I have this User
model:
class User
{
public $id;
public $email;
public $password;
public $errors;
public function isValid()
{
if (strpos($this->email, '@') === false) {
$this->errors['email'] = 'Please enter an email address';
}
if (!$this->password) {
$this->errors['password'] = 'Please enter a password';
} elseif (strlen($this->password) < 4) {
$this->errors['password'] = 'Please enter a longer password';
}
return !$this->errors;
}
}
And let's say I have this UserDAO
for retrieving, adding, updating, and deleting users:
class UserDAO
{
protected $conn;
protected $logger;
public function __construct(PDO $dbh, Logger $logger)
{
$this->dbh = $dbh;
$this->logger = $logger;
}
public function getUsers()
{
$rows = null;
try {
$rows = $this->dbh->query("SELECT * FROM users")->fetchAll();
} catch (PDOException $e) {
$this->logger->log($e->getMessage(), __METHOD__);
}
return $rows;
}
public function getUserById($id)
{
$row = null;
try {
$sth = $this->dbh->prepare("SELECT * FROM users WHERE id = ?");
$sth->execute(array($id));
$row = $sth->fetchObject('User');
} catch (PDOException $e) {
$this->logger->log($e->getMessage(), __METHOD__);
}
return $row;
}
public function addUser(User &$user)
{
$success = false;
try {
$sth = $this->dbh->prepare("
INSERT INTO users (email, password) VALUES (?, ?)
");
$sth->execute(array($user->email, $user->password));
if ($success = (bool) $sth->rowCount()) {
$user->id = $this->dbh->lastInsertId();
}
} catch (PDOException $e) {
$this->logger->log($e->getMessage(), __METHOD__);
}
return $success;
}
public function updateUser(User $user)
{
// ...
}
public function deleteUser($id)
{
// ...
}
public function isEmailUnique($email)
{
$count = 0;
try {
$sth = $this->dbh->prepare("SELECT COUNT(id) FROM users WHERE email = LOWER(?)");
$sth->execute(array($email));
$count = $sth->fetchColumn();
} catch (PDOException $e) {
$this->logger->log($e->getMessage(), __METHOD__);
}
return !$count;
}
}
When I process a form, I typically do something like this:
// ...
$userDAO = new UserDAO($dbh, $logger);
$user = new User();
$user->email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
$user->password = filter_input(INPUT_POST, 'password');
// validate user
if ($user->isValid()) {
// check if email address is unique (SO UGLY!)
if ($userDAO->isEmailUnique($user->email)) {
$user->errors['email'] = 'Please use a different email address';
}
// save user
if ($user->addUser($user)) {
// ...
} else {
// ...
}
} else {
// do something with $user->errors
}
This works great for simple cases, but how can I improve this? In particular, how can I do validations that require database access? For example, if I wanted to check that the email is unique and make it part of my validation, what would be a better way of doing this?