Tuesday, July 15, 2008

Domain Model Validation

Using some magic methods can integrate validation without having to implement get and set methods everywhere.
class ValidationException extends Exception {
}

abstract class DomainModel {
    private $validationErrors = array();

    final public function __get($field) {
        $getMethodName = 'get' . ucfirst($field);
        if (method_exists($this, $getMethodName)) {
            return call_user_func(array($this, $getMethodName));
        } elseif (isset($this->$field)) {
            return $this->$field;
        }
    }

    final public function __set($field, $value) {
        $setMethodName = 'set' . ucfirst($field);
        if (array_key_exists($field, $this->validationErrors)) {
            unset($this->validationErrors[$field]);
        }
        if (method_exists($this, $setMethodName)) {
            try {
                call_user_func(array($this, $setMethodName), $value);
            } catch (ValidationException $e) {
                $this->validationErrors[$field] = $e->getMessage();
            }
        } elseif (array_key_exists($field, get_object_vars($this))) {
            $this->$field = $value;
        }
    }

    final public function isValid() {
        return count($this->validationErrors) == 0;
    }

    final public function getValidationErrors() {
        return array_values($this->validationErrors);
    }

    final public function setData($data) {
        foreach ($data as $field => $value) {
            $this->__set($field, $value);
        }
    }

    final public function getData() {
        return get_object_vars($this);
    }
}

class Test extends DomainModel {
    protected $message;

    public function setMessage($msg) {
        if ($msg == null || strlen(trim($msg)) == 0) {
            throw new ValidationException("Message must not be empty");
        }
        $this->message = $msg;
    }
}

$obj = new Test();
$obj->message = "Hello!";
echo $obj->message . "\n";
$obj->message = " ";
if (!$obj->isValid()) {
    foreach ($obj->getValidationErrors() as $error) {
        echo $error . "\n";
    }
}