I worked on several Laravel projects and in almost every project there was a need for some sort of access management control system. Most basic example is validation whether some resource belongs to user. For instance, imagine you have site for freelancers where users can manage their projects, timelogs, invoices etc. When user wants to submit new timelog for some project he needs to select that project from dropdown menu and populate rest of the form. When he submits that form to an API we need to properly validate that request. Let’s say that dropdown menu has its name property set to “project_id”.
$rules = [ // other rules 'project_id' => 'required|exists:projects,id', // other rules ];
The problem
Next important step is to validate whether that project belongs to authenticated user. Now, this is where you have a lot of choices. You can use Illuminate\Auth\Access\Gate class and define user Abilities (see more) or you can write your custom checks in controllers. In my case, I wanted something that requires far less code and can be used as a validation rule. That would solve 2 problems at once:
- Reusable code that can be applied to almost every resource
- Effortless displaying errors regarding non authorized request
Solution
Basically, to validate whether some resource belongs to user I can do something like this:
$rules = [ // other rules 'project_id' => 'required|exists:projects,id|belongs_to_user:projects', // other rules ];
What this belongs_to_user validation rule does is checking whether given project id matches id in projects table. It also checks if user_id is the id of authenticated user. To add this custom validation rule we need to add custom service provider class with following command:
php artisan make:provider CustomValidationServiceProvider
Next step is to register new service provider class. That can be done by opening config/app.php file and adding App\Providers\CustomValidationServiceProvider::class to providers array. Now, new file will be created in app/Providers directory. Open newly generated file and add this code into body of boot method:
public function boot() { Validator::extend('belongs_to_user', function($attribute, $value, $parameters, $validator) { $table = $parameters[0]; $primary_key_field = isset($parameters[1]) ? $parameters[1] : 'id'; $user_foreign_key_field = isset($parameters[2]) ? $parameters[2] : 'user_id'; return DB::table($table)->where($primary_key_field, '=', $value)->first()->$user_foreign_key_field == Auth::user()->id; }); }
If you look at the code, there are 2 optional parameters. One parameter is for specifying custom primary key field for our resource (if your primary key field is not “id”). Other parameter is for specifying custom user foreign key field (if you’re not using user_id as foreign key for users table).
And that’s it. We added new validation rule with few lines of code. I hope this article will be helpful. Thanks for reading! Looking forward to your comments!
This custom validation rule implementation shows really great example of code reusability.
Also, it seems like this custom validation role is a must when developing bigger applications to avoid repeating yourself with checking user’s permissions (like “belongs to”) in the authorize() method of Laravel’s request class – instead, your solution solves this problem in the rules() method of request class. Really elegant solution. Cheers 🙂
P.S.
‘project_id’ => ‘required|exists:projects,id|belongs_to_user:projects,custom_id,custom_user_id’
– is that right for changing the default project id column and user id column?
Thanks for comment!
Yes, that’s right.
I like this implemention to create custom validation rules for Validator service. Thanks for that common example.
Thanks for the great solution
But we must consider the situations that admin has access to all records.
This is more suitable when user can define relationship between his models. This rule is for preventing user from choosing someone else’s model instance.
Illuminate\Auth\Access\Gate ı am learn very thankss..
Thank you for your valuable sharing. A nice guide is prepared.