Если поставить Laravel Starter Kit с Livewire внутри, который мы в прошлый раз немного поизучали, то у нас сразу, из коробки будет поддержка аутентификации. Но — только по электронной почте и паролю. А заказчики могут хотеть (и хотят) чтобы можно было входить с помощью логина, либо вместо почты, либо в качестве альтернативы. Причины могут быть разные, не будем зацикливаться. Вместо этого давайте посмотрим, как сделать, чтобы можно было использовать и логин, и почту в качестве идентификатора пользователя при входе.
Начнём как положено, с тестов. В tests/Feature/Auth
у нас есть AuthenticationTest.php
, в котором уже находятся дефолтные тесты аутентификации. Выберем главный опорный тест, на базе которого проведем изменения:
public function test_users_can_authenticate_using_the_login_screen(): void
{
$user = User::factory()->create();
$response = LivewireVolt::test('auth.login')
->set('email', $user->email)
->set('password', 'password')
->call('login');
$response
->assertHasNoErrors()
->assertRedirect(route('platform.main', absolute: false));
$this->assertAuthenticated();
}
мы видим что тест проверяет, что пользователь может задать на экране логина свой мейл и пароль, и тогда он войдет на сайт. Допустим, новое поле ввода (вместо почты) у нас будет называться login
и мы хотим использовать его как для почты, так и для имени пользователя. В качестве имени будем использовать $user->name
, который тоже есть из коробки.
Изменим наш тест так, чтобы получилось два:
public function test_users_can_authenticate_using_the_login_screen(): void
{
$user = User::factory()->create();
$response = LivewireVolt::test('auth.login')
->set('login', $user->email)
->set('password', 'password')
->call('login');
$response
->assertHasNoErrors()
->assertRedirect(route('platform.main', absolute: false));
$this->assertAuthenticated();
}
public function test_users_can_authenticate_with_name(): void
{
$user = User::factory()->create();
$response = LivewireVolt::test('auth.login')
->set('login', $user->name)
->set('password', 'password')
->call('login');
$response
->assertHasNoErrors()
->assertRedirect(route('platform.main', absolute: false));
$this->assertAuthenticated();
}
У нас есть два теста, проверяющие, что если пользователь введет в поле login
свое имя или почту, а также верный пароль, он успешно войдет на сайт. Однако пока эта логика не реализована. Реализуем её.
Все изменения будут сосредоточены в одном Volt-компоненте resources/views/livewire/auth/login.blade.php
. Поэтому приведу сразу измененный код компонента с комментариями:
// ...
new #[Layout('components.layouts.auth')] class extends Component {
// Изменим свойство $email на $login, уберем email из аннотации Validate, т.к. теперь можно ввести не только почту
#[Validate('required|string')]
public string $login = '';
// ...
/**
* Handle an incoming authentication request.
*/
// Мы переименовали метод login() в authenticate(), чтобы не было конфликта с полем $login.
// Если этого не сделать, компонент забуксует и работать не будет, и даже ошибку не бросит ни в бэке, ни в js.
public function authenticate(): void
{
$this->validate();
$this->ensureIsNotRateLimited();
// Определим, ввёл пользователь email или имя, для этого попробуем проверить $this->login фильтром по email.
// Если это имейл, в массиве $credentials укажем ключ email, иначе укажем name.
$credentials = filter_var($this->login, FILTER_VALIDATE_EMAIL)
? ['email' => $this->login, 'password' => $this->password]
: ['name' => $this->login, 'password' => $this->password];
// и передадим массив $credentials для авторизации
if (!Auth::attempt($credentials, $this->remember)) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
// Здесь мы тоже поменяли ключ с email на login, поскольку инпута email в форме логина больше нет.
'login' => __('auth.failed'),
]);
}
// ...
}
// ...
/**
* Get the authentication rate limiting throttle key.
*/
protected function throttleKey(): string
{
// Тут тоже поменяли поле email на login
return Str::transliterate(Str::lower($this->login) . '|' . request()->ip());
}
А в вёрстке компонента мы просто меняем инпут имейла на login (меняем атрибут type, убираем autocomplete):
<!-- ... -->
<form wire:submit="authenticate" class="flex flex-col gap-6">
<!-- Login -->
<flux:input
wire:model="login"
:label="'Логин или email'"
type="text"
required
autofocus
placeholder="Имя пользователя или email@example.com"
/>
<!-- ... -->
Да, и поскольку теперь мы используем для входа $user->name
, нужно сделать поле уникальным (в дефолтной миграции этого нет). Нужно создать новую миграцию и добавить в метод up()
код:
Schema::table('users', function (Blueprint $table) {
$table->unique('name');
});
Также стоит добавить 'unique:' . User::class
в правила валидации компонента resources/views/livewire/auth/register.blade.php
, чтобы юзеры не видели ошибку http 500, когда введут имя, которое уже есть. Ну и если вы используете еще какие-то формы создания или редактирования пользователя, там тоже стоит добавить валидацию на уникальность.
Напоследок запустите все тесты в AuthenticationTest.php
и исправить email
на login
в оставшихся тестах, чтобы они проходили.