skip to Main Content

I have tried to replace laravel/ui into livewire component.
RegisterController.php and ResetPasswordController.php are the target to replace.

Laravel version is 10.21.0 and Livewire version is v3.0.5.

First of all, following is the partial of route/web.php:

Auth::routes([
    'login' => true, 'logout' => true, 'confirm' => true, 'verify' => true,
]);

Route::middleware('guest')->group(function () {
    Route::get('register', AppLivewireAuthRegisterComponent::class)
        ->name('register');
    Route::post('register', [AppLivewireAuthRegisterComponent::class, 'register']);
    Route::get('password/reset/{token}', AppLivewireAuthResetPasswordComponent::class)
        ->name('password.reset');
    Route::post('password/reset', [AppLivewireAuthResetPasswordComponent::class, 'update'])
        ->name('password.update');
});

AppLivewireAuthRegisterComponent.php is based from IlluminateFoundationAuthRegistersUsers.php.
The change is that I need to validate $name, $email and $password instead of $request as follows:

<?php

namespace AppLivewireAuth;

use ...

class RegisterComponent extends Component
{
    use RedirectsUsers;

    public string $title = '';
    public string $name = '';
    public string $email = '';
    public string $password = '';

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = '/email/verify';

    /**
     * Handle a registration request for the application.
     *
     * @param Request $request
     *
     * @return Application|IlluminateFoundationApplication|RedirectResponse|Redirector
     *
     * @throws ValidationException
     */
    public function register(Request $request)
    {
        $this->validator()->validate();

        event(new Registered($user = $this->create()));

        $this->guard()->login($user);

        return redirect($this->redirectPath());
    }

    /**
     * Get the password reset validation rules.
     *
     * @return array
     */
    public function rules(): array
    {
        return [
            User::NAME => ['required','string','max:255'],
            User::EMAIL => ['required','string','email','max:255','unique:users'],
            User::PASSWORD => ['required',Password::min(12)->letters()->mixedCase()->numbers()->symbols()->uncompromised(),'zxcvbn:3'],
        ];
    }

    public function render(): View
    {
        return view('livewire.auth.register-component')
            ->layout('components.layouts.auth');
    }

    public function mount(): void
    {
        $this->title = config('app.name') . ' - ' . 'Registration';
    }

    /**
     * Get a validator for an incoming registration request.
     */
    protected function validator(): IlluminateContractsValidationValidator
    {
        return Validator::make([
            User::NAME => $this->name,
            User::EMAIL => $this->email,
            User::PASSWORD => $this->password,
        ], $this->rules());
    }

    /**
     * Get the guard to be used during registration.
     *
     * @return StatefulGuard
     */
    protected function guard(): StatefulGuard
    {
        return Auth::guard();
    }

    /**
     * The user has been registered.
     *
     * @param  mixed  $user
     * @return mixed
     */
    protected function registered($user)
    {
        //
    }

    /**
     * Create a new user instance after a valid registration.
     */
    protected function create(): User
    {
        return User::create([
            User::NAME => $this->name,
            User::EMAIL => $this->email,
            User::PASSWORD => Hash::make($this->password),
            User::API_TOKEN => Str::random(80),
            User::ROLE_ID => RoleType::USER,
        ]);
    }
}

And the part of view corresponding with this component is as follows:

                    <form wire:submit="register">
                        <div class="form-group row py-2">
                            <label for="name" class="col-md-4 col-form-label text-md-end">{{ __('Name') }}</label>
                            <div class="col-md-6">
                                <input wire:model="name" required autofocus type="text" class="form-control @error('name') is-invalid @enderror" id="name" autocomplete="name" />
                                @error('name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row py-2">
                            <label for="email" class="col-md-4 col-form-label text-md-end">{{ __('E-Mail Address') }}</label>
                            <div class="col-md-6">
                                <input wire:model="email" required type="email" class="form-control @error('email') is-invalid @enderror" id="email" autocomplete="email" />
                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row py-2">
                            <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
                            <div class="col-md-6 input-group">
                                <input wire:model="password" required type="password" class="form-control @error('password') is-invalid @enderror" id="password" autocomplete="current_password" />
                                <span class="mt-1 small">
                                    {{ __('application.user.columns.password.note') }}
                                </span>
                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row py-2">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-outline-warning">
                                    {{ __('Sign up') }}
                                </button>
                            </div>
                        </div>
                    </form>

AppLivewireAuthResetPasswordComponent.php is also based from IlluminateFoundationAuthResetsPasswords.php. This is also to validate $token, $email and $password instead of $request, too.

mount() method try to solve $token from the route.

<?php

namespace AppLivewireAuth;

use ...

class ResetPasswordComponent extends Component
{
    use RedirectsUsers;
    use RedirectByStatus;

    public const TOKEN = 'token';

    public string $token = '';
    public string $email = '';
    public string $password = '';

    /**
     * Where to redirect users after resetting their password.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    public function update(Request $request)
    {
        $validated = $this->validator()->validate();

        // Here we will attempt to reset the user's password. If it is successful we
        // will update the password on an actual user model and persist it to the
        // database. Otherwise we will parse the error and return the response.
        $response = $this->broker()->reset(
            $this->credentials(), function ($user, $password) {
                $this->resetPassword($user, $password);
            }
        );

        // If the password was successfully reset, we will redirect the user back to
        // the application's home authenticated view. If there is an error we can
        // redirect them back to where they came from with their error message.
        return $response == IlluminateSupportFacadesPassword::PASSWORD_RESET
            ? $this->sendResetResponse($request, $response)
            : $this->sendResetFailedResponse($request, $response);
    }

    /**
     * Get the password reset validation rules.
     *
     * @return array
     */
    public function rules(): array
    {
        return [
            self::TOKEN => ['required'],
            User::EMAIL => ['required','email'],
            User::PASSWORD => ['required',Password::min(12)->letters()->mixedCase()->numbers()->symbols()->uncompromised(),'zxcvbn:3'],
        ];
    }

    /**
     * Get the broker to be used during password reset.
     *
     * @return PasswordBroker
     */
    public function broker()
    {
        return IlluminateSupportFacadesPassword::broker();
    }

    public function render(): View
    {
        return view('livewire.auth.reset-password-component')
            ->layout('components.layouts.auth');
    }

    public function mount(string $token): void
    {
        $this->token = $token;
        $this->title = config('app.name') . ' - ' . 'Reset Password';
    }

    /**
     * Get a validator for an incoming registration request.
     */
    protected function validator(): IlluminateContractsValidationValidator
    {
        return Validator::make([
            self::TOKEN => $this->token,
            User::EMAIL => $this->email,
            User::PASSWORD => $this->password,
        ], $this->rules());
    }

    /**
     * Get the password reset validation error messages.
     *
     * @return array
     */
    protected function validationErrorMessages()
    {
        return [];
    }

    /**
     * Get the password reset credentials from the request.
     *
     * @param Request $request
     * @return array
     */
    protected function credentials()
    {
        return [
            self::TOKEN => $this->token,
            User::EMAIL => $this->email,
            User::PASSWORD => $this->password,
        ];
    }

    /**
     * Reset the given user's password.
     *
     * @param  IlluminateContractsAuthCanResetPassword  $user
     * @param  string  $password
     * @return void
     */
    protected function resetPassword($user, $password)
    {
        $this->setUserPassword($user, $password);

        $user->setRememberToken(Str::random(60));

        $user->save();

        event(new PasswordReset($user));

        $this->guard()->login($user);
    }

    /**
     * Set the user's password.
     *
     * @param  IlluminateContractsAuthCanResetPassword  $user
     * @param  string  $password
     * @return void
     */
    protected function setUserPassword($user, $password)
    {
        $user->password = Hash::make($password);
    }

    /**
     * Get the response for a successful password reset.
     *
     * @param Request $request
     * @param  string  $response
     * @return RedirectResponse|IlluminateHttpJsonResponse
     */
    protected function sendResetResponse(Request $request, $response)
    {
        return redirect($this->redirectPath())
            ->with('status', trans($response));
    }

    /**
     * Get the response for a failed password reset.
     *
     * @param Request $request
     * @param  string  $response
     * @return RedirectResponse
     */
    protected function sendResetFailedResponse(Request $request, $response)
    {
        return redirect()->back()
            ->withInput($request->only('email'))
            ->withErrors(['email' => trans($response)]);
    }

    /**
     * Get the guard to be used during password reset.
     *
     * @return StatefulGuard
     */
    protected function guard()
    {
        return Auth::guard();
    }
}

And the part of view corresponding with this component is as follows:

                    <form wire:submit="update">
                        <div class="form-group row">
                            <div class="col-md-6">
                                <input wire:model="token" type="hidden" class="form-control @error('token') is-invalid @enderror" id="token" />
                                @error('token')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row py-2">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
                            <div class="col-md-6">
                                <input wire:model="email" required autofocus type="email" class="form-control @error('email') is-invalid @enderror" id="email" autocomplete="email" />
                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row py-2">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
                            <div class="col-md-6 input-group">
                                <input wire:model="password" required type="password" class="form-control @error('password') is-invalid @enderror" id="password" autocomplete="current_password" />
                                <span class="mt-1 small">
                                    {{ __('application.user.columns.password.note') }}
                                </span>
                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row py-2 mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Reset Password') }}
                                </button>
                            </div>
                        </div>
                    </form>

I also wrote a test for these components.
RegisterComponentTest.php is for RegisterComponent.php.

<?php

namespace FeatureLivewireAuth;

use AppLivewireAuthRegisterComponent;
use AppModelsUser;
use LivewireLivewire;
use TestsTestCase;

class RegisterComponentTest extends TestCase
{
    /**
     * @test
     */
    public function renders_successfully()
    {
        Livewire::test(RegisterComponent::class)
            ->assertStatus(200);
    }
}

And ResetPasswordComponentTest.php is for ResetPasswordComponent.php.

<?php

namespace FeatureLivewireAuth;

use AppLivewireAuthResetPasswordComponent;
use AppModelsUser;
use LivewireLivewire;
use TestsTestCase;

class ResetPasswordComponentTest extends TestCase
{
    /**
     * @test
     */
    public function renders_successfully()
    {
        $token = 'token';

        Livewire::test(ResetPasswordComponent::class)
            ->call('mount', $token)
//            ->set('token', $token)
            ->assertStatus(200);
    }
}

Finally everything is on the table.
When I ran ./vendor/bin/phpunit tests/Feature/Livewire/Auth/RegisterComponentTest.php, all the test passed but when I ran ./vendor/bin/phpunit tests/Feature/Livewire/Auth/ResetPasswordComponentTest.php, the following error was thrown.

There was 1 error:

1) FeatureLivewireAuthResetPasswordComponentTest::renders_successfully
IlluminateViewViewException: Unable to resolve dependency [Parameter #0 [ <required> string $token ]] in class AppLivewireAuthResetPasswordComponent (View: /var/www/html/storage/framework/views/a168b0ebcff266df6b662fc4b6d06e88.blade.php)

/var/www/html/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:188
/var/www/html/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendedCompilerEngine.php:60
/var/www/html/vendor/laravel/framework/src/Illuminate/View/Engines/PhpEngine.php:60
/var/www/html/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendedCompilerEngine.php:22
/var/www/html/vendor/laravel/framework/src/Illuminate/View/Engines/CompilerEngine.php:72
/var/www/html/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendedCompilerEngine.php:10
/var/www/html/vendor/laravel/framework/src/Illuminate/View/View.php:195
/var/www/html/vendor/laravel/framework/src/Illuminate/View/View.php:178
/var/www/html/vendor/laravel/framework/src/Illuminate/View/View.php:147
/var/www/html/vendor/laravel/framework/src/Illuminate/View/Compilers/BladeCompiler.php:333
/var/www/html/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:353
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php:26
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/CallableDispatcher.php:40
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php:237
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php:208
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php:799
/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:141
/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:116
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php:800
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php:777
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php:741
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php:730
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:200
/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:141
/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:116
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:175
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:144
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php:564
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php:32
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/RequestBroker.php:30
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php:33
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/Render.php:24
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php:34
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php:17
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/Testable.php:35
/var/www/html/vendor/livewire/livewire/src/LivewireManager.php:157
/var/www/html/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:353
/var/www/html/tests/Feature/Livewire/Auth/ResetPasswordComponentTest.php:19
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:174

Caused by
IlluminateContractsContainerBindingResolutionException: Unable to resolve dependency [Parameter #0 [ <required> string $token ]] in class AppLivewireAuthResetPasswordComponent

/var/www/html/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:188
/var/www/html/vendor/livewire/livewire/src/ImplicitlyBoundMethod.php:21
/var/www/html/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36
/var/www/html/vendor/laravel/framework/src/Illuminate/Container/Util.php:41
/var/www/html/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93
/var/www/html/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:37
/var/www/html/vendor/livewire/livewire/src/Wrapped.php:23
/var/www/html/vendor/livewire/livewire/src/Features/SupportLifecycleHooks/SupportLifecycleHooks.php:125
/var/www/html/vendor/livewire/livewire/src/Features/SupportLifecycleHooks/SupportLifecycleHooks.php:20
/var/www/html/vendor/livewire/livewire/src/ComponentHook.php:19
/var/www/html/vendor/livewire/livewire/src/ComponentHookRegistry.php:45
/var/www/html/vendor/livewire/livewire/src/EventBus.php:60
/var/www/html/vendor/livewire/livewire/src/helpers.php:100
/var/www/html/vendor/livewire/livewire/src/Mechanisms/HandleComponents/HandleComponents.php:57
/var/www/html/vendor/livewire/livewire/src/LivewireManager.php:72
/var/www/html/storage/framework/views/d76adc34f3e5651d58b1e86454689044.php:7
/var/www/html/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php:124
/var/www/html/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php:125
/var/www/html/vendor/laravel/framework/src/Illuminate/View/Engines/PhpEngine.php:58
/var/www/html/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendedCompilerEngine.php:22
/var/www/html/vendor/laravel/framework/src/Illuminate/View/Engines/CompilerEngine.php:72
/var/www/html/vendor/livewire/livewire/src/Mechanisms/ExtendBlade/ExtendedCompilerEngine.php:10
/var/www/html/vendor/laravel/framework/src/Illuminate/View/View.php:195
/var/www/html/vendor/laravel/framework/src/Illuminate/View/View.php:178
/var/www/html/vendor/laravel/framework/src/Illuminate/View/View.php:147
/var/www/html/vendor/laravel/framework/src/Illuminate/View/Compilers/BladeCompiler.php:333
/var/www/html/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:353
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php:26
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/CallableDispatcher.php:40
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php:237
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php:208
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php:799
/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:141
/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:116
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php:800
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php:777
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php:741
/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php:730
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:200
/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:141
/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:116
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:175
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:144
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php:564
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php:32
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/RequestBroker.php:30
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php:33
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/Render.php:24
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php:34
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/InitialRender.php:17
/var/www/html/vendor/livewire/livewire/src/Features/SupportTesting/Testable.php:35
/var/www/html/vendor/livewire/livewire/src/LivewireManager.php:157
/var/www/html/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:353
/var/www/html/tests/Feature/Livewire/Auth/ResetPasswordComponentTest.php:19
/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:174

As you see, RegisterComponent.php and ResetPasswordComponent.php are very similar but I don’t know why RegisterComponentTest.php passes but ResetPasswordComponentTest.php fails.

Can anyone let me know what is the cause of error and how I should fix it.

Thanks in advance.

2

Answers


  1. The "Unable to resolve dependency" error in Laravel Livewire typically occurs when Livewire cannot automatically resolve a dependency for a component. This often happens when you are using dependency injection in your Livewire component’s constructor or when Livewire cannot determine the type or value of a property.

    Here are some common scenarios and solutions to resolve this error:

    https://askmequora.com/posts/how-to-solve-laravel-livewire-error-of-illuminate-view-viewexception-unable-to-resolve-dependency

    Login or Signup to reply.
  2. When I look at the docs it doesn’t seem like you should be calling the mount method via call, instead you should be passing data when initially setting up the test component:

    Livewire::test(ResetPasswordComponent::class, ['token' => $token])
        ->assertStatus(200);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search