I was asked how I deal with validation / create and update validation rulesets. Well here is one method I have used. Don't be afraid to build on top of what the framework has already given you. In my projects I use a base class for almost anything. You never know when you want your classes to inherit some common functionality. My BaseValidator
actually has some pretty useful methods and properties in it.
<?php
namespace FooProject\Internal\Validators;
use FooProject\Internal\Sanitizers\BaseSanitizer;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Validator;
abstract class BaseValidator
{
/**
* A collection of validation errors.
*
* @var Illuminate\Support\Collection
*/
protected $errors;
/**
* An array of sanitizers to be executed
* before the validation process.
*
* @var array
*/
protected $sanitizers = [];
/**
* Validation rules for this Validator.
*
* @var array
*/
protected $rules = [];
/**
* An array of custom validation messages.
*
* @var array
*/
protected $messages = [];
/**
* Set the intial errors collection.
*/
public function __construct()
{
$this->errors = new Collection;
}
/**
* Validate the provided data using the
* internal rules array.
*
* @param mixed $data
* @return bool
*/
public function validate($data, $ruleset = 'create')
{
// We allow collections, so transform to array.
if ($data instanceof Collection) {
$data = $data->toArray();
}
// Execute sanitizers over the data before validation.
$this->runSanitizers($data);
// Load the correct ruleset.
$rules = $this->rules[$ruleset];
// Create the validator instance and validate.
$validator = Validator::make($data, $rules, $this->messages);
if (!$result = $validator->passes()) {
$this->errors = $validator->messages();
}
// Return the validation result.
return $result;
}
/**
* Attach a sanitizer to this validation instance
* to be executed before the validation process.
*
* @param BaseSanitizer $sanitizer
* @return FooProject\Internal\Validators\BaseValidator
*/
public function attachSanitizer(BaseSanitizer $sanitizer)
{
$this->sanitizers[] = $sanitizer;
return $this;
}
/**
* Execute all of our registered sanitizers
* on the validation data.
*
* @param array $data
* @return void
*/
protected function runSanitizers($data)
{
foreach ($this->sanitizers as $sanitizer) {
$sanitizer->sanitize($data);
}
}
/**
* Return the error collection after a failed
* validation attempt.
*
* @return Illuminate\Support\Collection
*/
public function errors()
{
return $this->errors;
}
}
Bit confusing? Well here, take a look at an example validation class.
<?php
namespace FooProject\Internal\Validators;
use FooProject\Internal\Sanitizers\UsersSanitizer;
class UsersValidator extends BaseValidator
{
/**
* Validation rules for this Validator.
*
* @var array
*/
protected $rules = [
'create' => [
'username' => ['required', 'min:3', 'unique:users'],
'password' => ['required'],
'email' => ['required', 'email', 'unique:users']
],
'update' => [
'username' => ['min:3', 'unique:users'],
'email' => ['email', 'unique:users'],
'gender' => ['in:MALE,FEMALE']
]
];
/**
* Attach a default sanitizer to this
* validator instance.
*/
public function __construct()
{
parent::__construct();
$this->attachSanitizer(new UsersSanitizer);
}
}
The protected $rules
property contains a number of validation 'sets' that contain the traditional arrays of rules. Here we have a create and update set, where the update set has non required fields to allow for them to be missing in the 'PATCH' update format that API's use.
In the construct I add a Sanitizer. A sanitizer is a class I use to morph the input before validation, for example, a field may fail validation because it isn't uppercase, but we could easily do that ourselves and save the user an annoying error loop. So we use the Sanitizer to strtoupper() the field before we validate.
Here's an example of the validator usage.
$data = Input::get();
$validator = new UserValidator;
if ($validator->validate($data, 'update')) {
// validation passed
}
// validation failed
$errors = $validator->errors();
Remember, this is just my way of doing this, and it's not going to be perfect for everyone. It just happens to work for me.
Invent your own systems! Enjoy!