Description
At the moment, our AbstractValidator
class only validates attributes that are present in the $attributes
array
This works great for our STORE/PATCH endpoints the way we implement them in core.
On STORE, we first put the values in the model attribute by attribute, ensuring all keys are defined, even if the values are null or missing.
We then run the validator against $model->getAttributes()
, where all the keys are present.
On PATCH, we handle logic attribute by attribute and put the new values in the model only if it was provided.
We then run the validator against $model->isDirty()
which takes care of validating only what changed.
This issue is about extensions that have many attributes on a single model, or might not use models at all, and want to validate large amounts of attributes at once.
In this contexts extensions might want to use a variation of
$this->validator->assertValid($attributes);
$model->fill($attributes);
or
$model->fill($attributes);
$this->validator->assertValid($model->getAttributes());
Unfortunately here, if any attribute is missing (has no key in $attributes
at all), it will never be validated, unlike what would happen with a Laravel Validator.
This forces extensions to enumerate attributes at some point, either by setting $model->attr1 = Arr::get($attrs, 'attrs1'); $model->attr2 = Arr::get($attrs, 'attrs2') before calling $model->getAttributes(). Or alternatively, build an array like $attrs = ['attr1' => Arr::get($attrs, 'attrs1'), 'attr2' => Arr::get($attrs, 'attrs2')] before calling the validator on it and then $model->fill($attrs) it to the model.
In those situations, the Laravel Validator becomes the better solution over the Flarum Validator, but this means we loose the validating event from Flarum.
My suggestion is to add a way for validators to selectively validate all, or validate existing data.
Maybe something like:
abstract class AbstractValidator
{
// Can be overridden by classes to set their default
protected $validateMissingKeys = false;
// Can be used to switch the mode at run time
public function validateMissingKeys($validate = true)
{
$this->validateMissingKeys = $validate;
return $this;
}
// [...]
protected function makeValidator(array $attributes)
{
$rules = $this->validateMissingKeys ? $this->getRules() : Arr::only($this->getRules(), array_keys($attributes));
// [...]
}
}