Search code examples
phplaravelvalidationlaravel-formrequest

Laravel FormRequest validation gets User model and sometimes directly the name


I am using Laravel 8 and validate my requests with the form request validation. However, I have a strange problem. Before I am going to describe my problem I will paste my routes and my validation class here:

routes:

Route::post('users', ['as' => 'user.search', 'uses' => 'UserController@indexSearch']);

Route::post('user/name', ['as' => 'user.name.exists', 'uses' => 'UserController@nameExists']);

Route::post('user/email', ['as' => 'user.email.exists', 'uses' => 'UserController@emailExists']);

Route::post('user/email/check', ['as' => 'user.email.check', 'uses' => 'UserController@emailCheck']);

Route::get('user/{user:name}', ['as' => 'user.show', 'uses' => 'UserController@show']);

Route::get('user/{user:name}/einstellungen', ['as' => 'user.settings', 'uses' => 'UserController@showSettings']);

Route::get('user/{user:name}/produkte', ['as' => 'user.products', 'uses' => 'UserController@userProducts']);

Route::post('user/{user:name}/produkte', ['as' => 'user.products.post', 'uses' => 'UserController@userProducts']);

Route::get('user/{user:name}/saved/produkte', ['as' => 'user.marked.products', 'uses' => 'UserController@userMarkedProducts']);

Route::post('user/{user:name}/saved/produkte', ['as' => 'user.marked.products.post', 'uses' => 'UserController@userMarkedProducts']);

Route::post('user/{user:name}/avatar/upload', ['as' => 'user.avatar.update', 'uses' => 'UserController@update']);

Route::post('user/{user:name}/avatar/valid', ['as' => 'user.avatar.valid', 'uses' => 'UserController@validAvatar']);

Route::post('user/{user:name}/avatar/löschen', ['as' => 'user.avatar.delete', 'uses' => 'UserController@deleteAvatar']);

Route::post('user/{user:name}/update/password', ['as' => 'user.password.change', 'uses' => 'UserController@updatePassword']);

Route::post('user/{user:name}/validate/password', ['as' => 'user.password.validate', 'uses' => 'UserController@validatePassword']);

This is my FormRequest validation which checks if the user is authorized to perform this request and if the given data are valid. As you can see, I check if the Auth::user()->name is the same as the requested user ($this->user->name) to change the image. Because only the owner of his profile image should be allowed to change is profile image.

class UserAvatarUpdateRequest extends FormRequest
{
    public function authorize()
    {
        dump(array('auth' => Auth::user()->name, 'requested' => $this->user));     

        if(Auth::check() && Auth::user()->name == $this->user->name){
            return true;
        }

        return false;
    }
    
    public function rules()
    {
        return [
            'avatar' => [
                'required',
                'image',
                'max:5000',
                'mimes:jpeg,png,jpg',
                Rule::dimensions()->minWidth(200)->minHeight(200)->ratio(1)
            ],
        ];
    }

}

Now I will come to the problem. As you can see I have several routes for my user and there is one route called user.avatar.valid and another route called user.avatar.update.

Both routes, user.avatar.valid and user.avatar.update are using the FormRequest validation class I have pasted above. Basically user.avatar.valid is being called before the user submits the form to check if the uploaded image is valid. I know, I also could do this in one request but that's not possible because I am using vee-validate for Vuejs to validate if the form is valid.

However, thats not the probleme here. The problem is that when calling the user.avatar.valid route my dump echos for the key requested the name of the requested user. But, when performing a request for user.avatar.update I don't get the name of the user but rather the user object which contains the id, name, email and so on.

But exactly this is the point... when trying to call user.avatar.update I get a 403 error (forbidden) because $this->user is not equal to Auth::user()->name. Obviously it is not the same when I compare a string (name) to the user object.

However, when calling user.avatar.valid it passes because here I get the name when calling $this->user and not the user object.

But why is that?

When I change $this->user to $this->user->name I get the actual name for the user when calling the user.avatar.update route but on the other side I get a 500 error when calling the user.avatar.valid route because it says Trying to get property 'name' of non-object...

Does anybody has an idea where the problem here is and how to fix this?

Kind regards and thank you!


Solution

  • Make sure you are typehinting the property in your controller's method signature properly for Route Model Binding. If not you will just get the value of the segment, the name of the user, instead of the User model since the Implicit Model Binding won't take place:

    public function validAvatar(User $user) { ... }
    

    Laravel 8.x Docs - Routing - Route Model Bindings - Implicit Binding