我试着把laravel/ui替换成livewire组件。php和ResetPasswordController.php是要替换的目标。
Laravel版本是10.21.0,Livewire版本是v3.0.5。
首先,下面是route/web.php的部分内容:
Auth::routes([
'login' => true, 'logout' => true, 'confirm' => true, 'verify' => true,
]);
Route::middleware('guest')->group(function () {
Route::get('register', App\Livewire\Auth\RegisterComponent::class)
->name('register');
Route::post('register', [App\Livewire\Auth\RegisterComponent::class, 'register']);
Route::get('password/reset/{token}', App\Livewire\Auth\ResetPasswordComponent::class)
->name('password.reset');
Route::post('password/reset', [App\Livewire\Auth\ResetPasswordComponent::class, 'update'])
->name('password.update');
});
App\Livewire\Auth\RegisterComponent.php基于Illuminate\Foundation\Auth\RegistersUsers.php。更改是我需要验证$name、$email和$password而不是$request,如下所示:
<?php
namespace App\Livewire\Auth;
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|\Illuminate\Foundation\Application|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(): \Illuminate\Contracts\Validation\Validator
{
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,
]);
}
}
该组件对应的视图部分如下:
<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>
App\Livewire\Auth\ResetPasswordComponent.php也基于Illuminate\Foundation\Auth\ResetsPasswords.php。这也是为了验证$token、$email和$password,而不是$request。
mount()方法尝试从路由中解析$token。
<?php
namespace App\Livewire\Auth;
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 == \Illuminate\Support\Facades\Password::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 \Illuminate\Support\Facades\Password::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(): \Illuminate\Contracts\Validation\Validator
{
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 \Illuminate\Contracts\Auth\CanResetPassword $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 \Illuminate\Contracts\Auth\CanResetPassword $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|\Illuminate\Http\JsonResponse
*/
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();
}
}
该组件对应的视图部分如下:
<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>
我还为这些组件编写了一个测试。RegisterComponentTest.php用于RegisterComponent.php。
<?php
namespace Feature\Livewire\Auth;
use App\Livewire\Auth\RegisterComponent;
use App\Models\User;
use Livewire\Livewire;
use Tests\TestCase;
class RegisterComponentTest extends TestCase
{
/**
* @test
*/
public function renders_successfully()
{
Livewire::test(RegisterComponent::class)
->assertStatus(200);
}
}
而ResetPasswordComponentTest.php是ResetPasswordComponent.php的。
<?php
namespace Feature\Livewire\Auth;
use App\Livewire\Auth\ResetPasswordComponent;
use App\Models\User;
use Livewire\Livewire;
use Tests\TestCase;
class ResetPasswordComponentTest extends TestCase
{
/**
* @test
*/
public function renders_successfully()
{
$token = 'token';
Livewire::test(ResetPasswordComponent::class)
->call('mount', $token)
// ->set('token', $token)
->assertStatus(200);
}
}
最后,一切都摆在桌面上。当我运行./vendor/bin/phpunit tests/Feature/Livewire/Auth/RegisterComponentTest.php时,所有测试都通过了,但当我运行./vendor/bin/phpunit tests/Feature/Livewire/Auth/ResetPasswordComponentTest.php时,抛出了以下错误。
There was 1 error:
1) Feature\Livewire\Auth\ResetPasswordComponentTest::renders_successfully
Illuminate\View\ViewException: Unable to resolve dependency [Parameter #0 [ <required> string $token ]] in class App\Livewire\Auth\ResetPasswordComponent (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
Illuminate\Contracts\Container\BindingResolutionException: Unable to resolve dependency [Parameter #0 [ <required> string $token ]] in class App\Livewire\Auth\ResetPasswordComponent
/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
正如您所看到的,RegisterComponent.php和ResetPasswordComponent.php非常相似,但我不知道为什么RegisterComponentTest.php通过,而ResetPasswordComponentTest.php失败。
谁能告诉我错误的原因是什么,我应该如何修复它。
先谢了。
1条答案
按热度按时间zour9fqk1#
当我查看文档时,似乎不应该通过
call
调用mount
方法,而是应该在最初设置测试组件时传递数据: