How to use reusable validation in a ValueObject How to use reusable validation in a ValueObject php php

How to use reusable validation in a ValueObject


Example 4!

Why? Because it's testable, plain and simple.

Depending on what your validator actually does (in some circumstances your validator may rely on an API call or a call to a database) the injectable validator is completely testable via mocks. All of the other's are either impossible to test under the circumstances I just mentioned, or incredibly hard to test.

EDIT: For those wondering how the dependency injection method helps with testing then consider the CommentValidator class below that utilises a standard Akismet spam checking library.

class CommentValidator {    public function checkLength($text) {        // check for text greater than 140 chars        return (isset($text{140})) ? false : true;    }    public function checkSpam($author, $email, $text, $link) {        // Load array with comment data.        $comment = array(                        'author' => $author,                        'email' => $email,                        'website' => 'http://www.example.com/',                        'body' => $text,                        'permalink' => $link                );        // Instantiate an instance of the class.        $akismet = new Akismet('http://www.your-domain.com/', 'API_KEY', $comment);        // Test for errors.        if($akismet->errorsExist()) { // Returns true if any errors exist.            if($akismet->isError('AKISMET_INVALID_KEY')) {                    return true;            } elseif($akismet->isError('AKISMET_RESPONSE_FAILED')) {                    return true;            } elseif($akismet->isError('AKISMET_SERVER_NOT_FOUND')) {                    return true;            }        } else {            // No errors, check for spam.            if ($akismet->isSpam()) {                    return true;            } else {                    return false;            }        }    }}

And now below, when you're setting up your unit tests we have a CommentValidatorMock class that we use instead, we have setters to manually change the 2 output bools we can have, and we have the 2 functions from above mock'd up to output whatever we want without having to go through the Akismet API.

class CommentValidatorMock {    public $lengthReturn = true;    public $spamReturn = false;    public function checkLength($text) {        return $this->lengthReturn;    }    public function checkSpam($author, $email, $text, $link) {        return $this->spamReturn;    }    public function setSpamReturn($val) {        $this->spamReturn = $val;    }    public function setLengthReturn($val) {        $this->lengthReturn = $val;    }}

If you're serious about unit testing then you need to use DI.


The first instinct is usually the best. You should use the first option. EmailAddress is a value object. It can be reused in other value objects or entities. I don't understand why you think it's not reusable. You can have a "shared library" of these common value objects used in other bounded contexts. Just be careful what you put in there. They would need to be truly generic if that's even conceptually possible.


I think if you use separate validation methods or move the validators to separate class will be butter and prevent DRY

    class EmailAddress{      protected $value;      public function __construct ($value)      {        $this->value = \validateEmailAddress($value);      }    }    function validateEmailaddress(string $value) : string      {      if(!is_string($value)){        throw new \ValidationException('This is not an emailaddress.');      } // Wrong function, just an example      return $value;    }    //OR for strict OOP people    final class VOValidator{      private function __construct(){}      public static function validateEmailaddress(string $input): string{...}    }    //I will prefer even go far and use Either from (FP monads)     interface ValueObejctError {}    class InvalidEmail implements ValueObjectError {}    function validateEmailaddress(string $input): Either {     // it will be better if php supported generic so using Either<InvalidaEmail, string> is more readable but unfortunately php has no generic types, maybe in future      return is_string($input)         ? new Right($input)        : new Left(new InvalidEmail());    }