Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

LaraBABA's avatar

Rules problems with existing email

Hi all.

I have created 2 controllers, one userController.php(to change user's data from the admin side) and one profileController.php(for the users to change his own data).

When I log in as an admin and update the profile of a user, it works.

When I log in as a user and update the owned profile of the user, I keep seeing this error:

The email has already been taken.

Both controllers use the same rule but the email rule is not working the same way when the email already exists on the profile user page.

userController.php(letting duplicate emails passing through)

    /**
     * @param \App\Http\Requests\UserUpdateRequest $request
     * @param \App\Models\User $user
     * @return \Illuminate\Http\Response
     */
    public function update(UserUpdateRequest $request, User $user)
    {
        $this->authorize('update', $user);
        $validated = $request->validated();

        if (empty($validated['password'])) {
            unset($validated['password']);
        } else {
            $validated['password'] = Hash::make($validated['password']);
        }

        if ($request->hasFile('avatar')) {
            if ($user->avatar) {
                Storage::delete($user->avatar);
            }

            $validated['avatar'] = $request->file('avatar')->store('public');
        }

        $user->update($validated);

        $user->syncRoles($request->roles);

        return redirect()
            ->route('users.edit', $user)
            ->withSuccess(__('crud.common.saved'));
    }

profileController.php(NOT letting duplicate emails passing through)

    /**
     * @param UserUpdateRequest $request
     * @param User $user
     * @return mixed
     * @throws AuthorizationException
     */
    public function update(UserUpdateRequest $request, User $user)
    {
        $this->authorize('updateOwnProfile', $user);
        $validated = $request->validated();

        if (empty($validated['password'])) {
            unset($validated['password']);
        } else {
            $validated['password'] = Hash::make($validated['password']);
        }

        if ($request->hasFile('avatar')) {
            if ($user->avatar) {
                Storage::delete($user->avatar);
            }

            $validated['avatar'] = $request->file('avatar')->store('public');
        }
        $role = Role::where('name', 'user')->first();
        $user->update($validated);

        $user->syncRoles($role->id);

        return redirect()
            ->route('profile.edit', $user)
            ->withSuccess(__('crud.common.saved'));
    }

Routes:

        Route::resource('users', UserController::class);
        Route::resource('profile', ProfileController::class);

Both using the same rule request file UserUpdateRequest.php:

<?php

namespace App\Http\Requests;

use Illuminate\Validation\Rule;
use Illuminate\Foundation\Http\FormRequest;

class UserUpdateRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'first_name' => ['required', 'max:255', 'string'],
            'last_name' => ['required', 'max:255', 'string'],
            'email' => [
                'required',
                Rule::unique('users', 'email')->ignore($this->user),
                'email',
            ],
            'password' => ['nullable'],
            'business_name' => ['nullable', 'max:255', 'string'],
            'business_address_line_1' => ['nullable', 'max:255', 'string'],
            'business_address_line_2' => ['nullable', 'max:255', 'string'],
            'postcode' => ['nullable', 'max:255', 'string'],
            'business_phone' => ['nullable', 'max:255', 'string'],
            'vat_number' => ['nullable', 'max:255', 'string'],
            'active' => ['required', 'max:255', 'string'],
            'avatar' => ['nullable', 'image', 'max:1024'],
            'roles' => 'array',
        ];
    }
}

I used logger('test') before the rule return to see if both controllers were sending me to the same rule and both are correctly hitting the email rule but behaving differently. I cannot work out why :-(

Any idea please?

Thanks

0 likes
6 replies
ramonrietdijk's avatar

The resource route for users is creating a URL with a variable for user like users/{user}

Profile will instead create profile/{profile} meaning your $this->user variable will be null in your request and validation rule.

The rule below should work for you.

Rule::unique('users', 'email')->ignore($this->user ?? $this->profile),

You can see your routes with the following command:

php artisan route:list
LaraBABA's avatar

@ramonrietdijk Ohhh my ....very good point! I will try in the morning and let you know buddy!

Thank you so much!

1 like
LaraBABA's avatar

@ramonrietdijk Thanks I tried it,

  GET|HEAD        profile ................profile.index › ProfileController@index
  POST            profile ................ profile.store › ProfileController@store
  GET|HEAD        profile/create ................profile.create › ProfileController@create
  GET|HEAD        profile/{profile} ................ profile.show › ProfileController@show
  PUT|PATCH       profile/{profile}  ................ profile.update › ProfileController@update
  DELETE          profile/{profile} ................ profile.destroy › ProfileController@destroy
  GET|HEAD        profile/{profile}/edit ................ profile.edit › ProfileController@edit

If I var_dump() just before the rule I can see that the data is access in:

public function rules()
    {
var_dump('testing')
        return [
            'first_name' => ['required', 'max:255', 'string'],
            'last_name' => ['required', 'max:255', 'string'],
            'email' => [
                'required',
                Rule::unique('users', 'email')->ignore($this->user),
                'email',
            ],
            'password' => ['nullable'],
            'business_name' => ['nullable', 'max:255', 'string'],
            'business_address_line_1' => ['nullable', 'max:255', 'string'],
            'business_address_line_2' => ['nullable', 'max:255', 'string'],
            'postcode' => ['nullable', 'max:255', 'string'],
            'business_phone' => ['nullable', 'max:255', 'string'],
            'vat_number' => ['nullable', 'max:255', 'string'],
            'active' => ['required', 'max:255', 'string'],
            'avatar' => ['nullable', 'image', 'max:1024'],
            'roles' => 'array',
        ];
    }

But this method is never reached:

public function update(UserUpdateRequest $request, User $user)
    {
var_dump('Never reached')
        $this->authorize('updateOwnProfile', $user);
        $validated = $request->validated();

        if (empty($validated['password'])) {
            unset($validated['password']);
        } else {
            $validated['password'] = Hash::make($validated['password']);
        }

        if ($request->hasFile('avatar')) {
            if ($user->avatar) {
                Storage::delete($user->avatar);
            }

            $validated['avatar'] = $request->file('avatar')->store('public');
        }
        $role = Role::where('name', 'user')->first();
        $user->update($validated);

        $user->syncRoles($role->id);

        return redirect()
            ->route('profile.edit', $user)
            ->withSuccess(__('crud.common.saved'));
    }

It looks like I cannot use "UserUpdateRequest " inside the profileController update method, only in the UserController update method. I was trying not to duplicate the rule for 2 models but it looks like this UserUpdateRequest is somehow attached to the user Model only.

It is a bit sad if I have to recreate a new rule just for profileController knowing that it is identical to the userController rules.

Not sure why it is working this way.

ramonrietdijk's avatar

@User476820 Your profile controller expects a $profile variable just like in your route. Could you update the method in your controller and try again? Don't forget to include the || $this->profile in your rule like I mentioned above.

public function update(UserUpdateRequest $request, User $profile)
LaraBABA's avatar

@ramonrietdijk Based on what Martin said in the other post(link above), it will not work, only this can work:

public function update(ProfileUpdateRequest $request, Profile $profile)

It looks like the way it is built, Laravel only link a policy to a single model, what I was trying to achieve above is not repeat myself and reuse policies with various models in multiple controllers, but it seems that it is not possible. I am also very surprised that it works this way. Now I need to duplicate so much code between a USER model and a PROFILE model(a bit sad).

Please or to participate in this conversation.