I've been pondering how it might be possible to separate business logic relating to input validation and decided to experiment with the idea of using the Specifiation Design Pattern. Specifications are used for both validation and data lookup, but in this particular instance I'm starting with validation.
The base classes consist of a simple AssertableSpec
class for encapsulating a single specification,
and a CompositeAssertableSpec
class for combining multiple specifications.
This is a simple specification that checks that the provided Author ID corresponds to an existing author.
class AuthorExistsSpec extends AbstractSpec
{
/* snip */
public function isSatisfiedBy($candidate)
{
$author = $this->authorRepo->findById($candidate->author_id);
$satisfied = ($author->count() > 0);
return $satisfied;
}
}
This specification contains the business logic for creating and saving a new Post
instance and is composed of three child specifications, all of which must be satisfied before the specification itself is satisfied.
class SuitableForCreationSpec extends AndSpec
{
public function __construct()
{
$this->andSatisfiedBy(new AuthorExistsSpec)
->andSatisfiedBy(new SlugIsUniqueSpec)
->andSatisfiedBy(new HasPostAndBodySpec);
}
}
A repository might check against this specification during creation:
class DBPostRepository
{
/* snip */
public function create(array $data)
{
$creatable = new Spec\SuitableForCreationSpec();
$post = new Post();
$post->fill($data);
if($spec->isSatisfiedBy($post)) {
$post->save();
} else {
$this->handleValidationFailure($post, $spec->messages());
}
return $post;
}
##Todo
- Better Code documentation
- Implementing additional classes for some common use-cases
- A simple demo
##See Also