I have following rules for field named user_id
.
"rules" => ["zero_to_null", "integer", "min:1", "exists:app_users,id"],
user_id
is nullable, so it can be null
, or 1,2,3,etc.
When user selects '0' in the browser I want to set it as null
in database.
Can I make it using only rules?
So that if zero_to_null
rule passes (if value equals '0'), then all other rules are not checked.
Validator::extend("zero_to_null", function($attribute, $value, $parameters, $validator) {
if($value === "0") {} // do something
});
P.S. User sends in POST request 0
value and it must be set as NULL
in database. The question is only about rules, not about something like setUserIdAttribute($value)
. Field always exists in request, so sometimes
rule can not help me.
Model is validated using universal function across application.
public static function createValidatorForModel($cls, $data) {
$rules = $cls::getFieldsRules();
$validator = Validator::make($data, $rules);
$validator->setAttributeNames($cls::getFieldsLabels());
return $validator;
}
getFieldsRules
returns rules for $cls
Example call:
$validator = ModelValidationHelper::createValidatorForModel(User::class, $request->all());
You should not mix the concerns like that. The validation rule should only perform validation.
If you have nullable field you can either have a null passed from via something like this or let your controller mutate the necessary data. Of course it isn't always practical to force the underlying db structure into the external input, in such cases it should be the controller's responsibility of mutating the incoming data from the expected input format to the underlying model structure.
This also means that you should not be blindly passing the result of $request->all()
to your database. While the data might be validated and query parameter binding will protect your application from sql injections it will do nothing against forged requests that include more fields than you expect to handle during the specific request. The unexpected fields will also avoid any kind of validation.
Instead, your route handler (closure, controller method, etc.) should be explicit about the fields that it intends to handle:
public function action(Request $request)
{
/* Create Validator for this action */->validate();
$data = $request->only('fields', 'that', 'you', 'expect', 'to', 'handle');
/* Transform $data from input format to db format */
Model::create($data);
}
It is also worth nothing that while it's tempting to let the model dictate its own validation rules like what you're doing with the createValidatorForModel
method, such approach falls apart when different actions against the model have different restrictions (e.g. admin user can edit more fields than non-admin user). Additionally, the separation of validated and expected field lists makes it harder to spot gaps in validation.