rr-
rr-

Reputation: 14811

Where should validation take place?

I want to use MVC pattern in my PHP project. I also want to use Dao-Service pattern for Model layer since it makes database engines easily interchangeable and keeps business logic out of DB interaction.

Now, I heard that validation should happen in Model layer since Controllers are only responsible for transporting data. That's pretty reasonable.

Should it, however, be implemented in Service layer, or entities themselves?

Approach 1: validation in entities

class Post extends Entity
{
    protected $title;

    public function getTitle()
    {
        return $this->title;
    }

    public function setTitle($newTitle)
    {
        if (strlen($newTitle) == 0)
            throw new ValidationException('Title cannot be empty.');
        $this->title = $newTitle;
    }
}

class PostService
{
    public static function saveOrUpdate(Post $post)
    {
        PostDao::saveOrUpdate($post);
    }
}

Pros:

Cons:

Approach 2: validation in service

class Post extends Entity
{
    public $title;
}

class PostService
{
    public static function saveOrUpdate(Post $post)
    {
        if (strlen($post->title) == 0)
            throw new ValidationException('Title cannot be empty.');
        PostDao::saveOrUpdate($post);
    }
}

Pros:

Cons:

Do you have any hints, SO? Am I missing something? So far the project is on drawing board so I'm ready for anything.

Upvotes: 3

Views: 1666

Answers (2)

tereško
tereško

Reputation: 58444

NOTE: Controller is not responsible for "transporting data". Controller's responsibility is altering model's (and in special cases - current view instance's) state.

There is actually third approach: have a separate isValid() method for the domain object (you call them "entities"). The setter validation becomes messy, when you have validation rules across multiple data entries.

Example: validation of repeated password for user registration form.

Having validation in setter for this will turn out quite messy. Especially if you opt to use exceptions for each failing validation.

Also, instead of data access objects I would recommend for you to use data mappers. The code would basically look something like this:

$post = new Model\Domain\Post;
$mapper = new Model\Mappers\Post($pdo);

$post->setId(42);
$mapper->fetch($post);

$post->setTitle($title);
if ($post->isValid()) {
    $mapper->store($post);
}

This approach also lets you externalize the validation by injecting some kind of Validator instance in the Model\Domain\Post instance through constructor, if you want to reuse some validation rules.

Though, when making a larger app, you will probably notice, that there are very few repeated checks, which go beyond existing php filters.

NOTE: please don't use static classes. This forces you to use procedural paradigm, where it's not needed.

Another thing that you have to pay attention to is: what kind of validation are you doing ?

Business rules should be validated in domain objects, but data integrity checks (like: "is this email address unique") are part of persistence logic. And persistence logic should (according to SRP be handled by a separate instance. In the given example that part was managed by data mapper.

Upvotes: 4

thexacre
thexacre

Reputation: 692

I think you should have two layers of validation, your "model" validation which validates invariant conditions which apply globally to the data model. For example, "name" is required due to that being a not-null field in the database. In this instance approach 2 is appropriate.

The other layer of validation is "form" validation, and that would go in your form model, this tests for conditions which are specific to the form. Say for example you have an is_admin field on your user model, it might be valid to set this in the admin panel update user form, but invalid on the users "change password" form. This approach is probably closer to the first.

With regard to implementing it, I probably wouldn't validate it in the setter unless you want to have a test around every single "set" or you're happy to set and save only the fields which are valid. Usually you'd reject the entire update if one of the fields is invalid, so it would make more sense to have an isValid() function, which could be called automatically before saving or to test whether it's valid without saving.

Upvotes: 2

Related Questions