Мини-блог на Laravel, часть 8. Регистрация и аутентификация пользователей на сайте

25.09.2020

Теги: LaravelMySQLPHPWeb-разработкаАутентификацияБазаДанныхБлогПользовательПрактикаФормаФреймворк

В Laravel сделать аутентификацию очень просто — почти всё готово из коробки. Мы уже установили ранее пакет laravel/ui, чтобы использовать в шаблонах фреймворк bootstrap. Для создания заготовок всех необходимых для аутентификации контроллеров, шаблонов и роутов нужно выполнить artisan-команду.

> php artisan ui:auth
> npm install && npm run dev

Будут созданы контроллеры:

  • RegisterController — обеспечивает регистрацию пользователей
  • LoginController — обеспечивает аутентификацию пользователей
  • ForgotPasswordController — отправляет письмо на сброс пароля
  • ResetPasswordController — содержит логику для сброса паролей

Будут созданы шаблоны:

  • auth.register — форма регистрации пользователей
  • auth.login — форма аутентификации пользователей
  • auth.passwords.email — форма для ввода адреса почты (восстановление пароля)
  • auth.passwords.reset — форма для ввода нового пароля (восстановление пароля)

Будут добавлены маршруты:

Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');

Вызов Auth::routes() добавляет сразу около дюжины маршрутов, посмотреть эти маршруты можно в классе Laravel\Ui\AuthRouteMethods.

Кроме того, будет добавлен контроллер HomeController, отвечающий за показ страницы /home. После регистрации и аутентификации пользователи перенаправляются на эту страницу. Нам этот контроллер не нужен, так что можно его удалить. Также можно удалить шаблоны home.blade.php и app.blade.php — но пока не будем этого делать, нам нужно кое-что забрать оттуда.

Сейчас пользователи уже могут регистрироваться и аутентифицироваться. Если открыть в браузере страницу /register, то будет показана форма регистрации нового пользователя.

Но не хватает перевода на русский язык. Чтобы получить языковые файлы, установим пакет

> composer require laravel-lang/lang:~7.0

После этого нужно скопировать директорию vendor/laravel-lang/lang/src/ru и файл vendor/laravel-lang/lang/json/ru.json в директорию resources/lang. И смотрим на форму регистрации пользователей еще раз.

Теперь посмотрим, что находится в layout-шаблоне app.blade.php. Нас интересует следующий фрагмент кода:

<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
    <!-- Authentication Links -->
    @guest
        <li class="nav-item">
            <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
        </li>
        @if (Route::has('register'))
            <li class="nav-item">
                <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
            </li>
        @endif
    @else
        <li class="nav-item dropdown">
            <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown"
               aria-haspopup="true" aria-expanded="false" v-pre>
                {{ Auth::user()->name }}
            </a>
            <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                <a class="dropdown-item" href="{{ route('logout') }}"
                   onclick="event.preventDefault();
                                 document.getElementById('logout-form').submit();">
                    {{ __('Logout') }}
                </a>
                <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
                    @csrf
                </form>
            </div>
        </li>
    @endguest
</ul>

Скопируем его и вставим в наш layout-шаблон site.blade.php:

<!doctype html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{{ $title ?? 'Веб-разработка' }}</title>
    <link rel="shortcat icon" type="image/png" href="{{ asset('favicon.png') }}"/>
    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    <script src="{{ asset('js/app.js') }}"></script>
</head>
<body>
<div class="container">
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <a class="navbar-brand" href="{{ route('blog.index') }}">Веб-разработка</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse"
                data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
                aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <!-- ссылки слева -->
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item">
                    <a class="nav-link" href="#">Авторы</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="{{ route('post.create') }}">Создать</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Контакты</a>
                </li>
            </ul>

            <!-- форма поиска -->
            <form class="form-inline my-2 my-lg-0" action="{{ route('post.search') }}">
                <input class="form-control mr-sm-2" type="search" name="search"
                       placeholder="Найти пост..." aria-label="Поиск">
                <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Поиск</button>
            </form>

            <!-- ссылки справа -->
            <ul class="navbar-nav ml-auto">
            @guest
                <li class="nav-item"> <!-- ссылка для входа -->
                    <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                </li>
                @if (Route::has('register'))
                    <li class="nav-item"> <!-- ссылка для регистрации -->
                        <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                    </li>
                @endif
            @else
                <li class="nav-item">
                    <a class="nav-link" href="{{ route('logout') }}"
                       onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
                        {{ __('Logout') }} <!-- ссылка выхода -->
                    </a>
                    <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
                        @csrf
                    </form>
                </li>
            @endguest
            </ul>
        </div>
    </nav>

    @if ($message = Session::get('success'))
        <div class="alert alert-success alert-dismissible mt-4" role="alert">
            <button type="button" class="close" data-dismiss="alert" aria-label="Закрыть">
                <span aria-hidden="true">&times;</span>
            </button>
            {{ $message }}
        </div>
    @endif

    @if ($errors->any())
        <div class="alert alert-danger alert-dismissible mt-4" role="alert">
            <button type="button" class="close" data-dismiss="alert" aria-label="Закрыть">
                <span aria-hidden="true">&times;</span>
            </button>
            <ul class="mb-0">
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    @yield('content')
</div>
</body>
</html>

Теперь посмотрим на шаблон home.blade.php:

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Dashboard') }}</div>
                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif
                    {{ __('You are logged in!') }}
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Здесь должно показываться flash-сообщение, но flash-переменная не установлена, так что давайте это исправим.

class RegisterController extends Controller {
    /* ... */

    /**
     * Сразу после регистрации выполняем редирект и устанавливаем flash-сообщение
     */
    protected function registered(Request $request, $user) {
        return redirect($this->redirectTo)
            ->with('status', trans('auth.success'));
    }
}
class LoginController extends Controller {
    /* ... */

    /**
     * Сразу после входа выполняем редирект и устанавливаем flash-сообщение
     */
    protected function authenticated(Request $request, $user) {
        return redirect($this->redirectTo)
            ->with('status', __('You are logged in!'));
    }

    /**
     * Сразу после выхода выполняем редирект и устанавливаем flash-сообщение
     */
    protected function loggedOut(Request $request) {
        return redirect($this->redirectTo)
            ->with('status', trans('auth.loggedout'));
    }
}

Для сообщения «You are logged in!» нашелся подходящий перевод в файле ru.json, а вот сообщения об успешной регистрации и выходе из системы найти не удалось, так что добавим ключи regsuccess и loggedout в файл resources/lang/ru/auth.php.

return [
    'failed'     => 'Неверное имя пользователя или пароль.',
    'throttle'   => 'Слишком много попыток входа. Пожалуйста, попробуйте еще раз через :seconds секунд.',
    'regsuccess' => 'Регистрация прошла успешно!',
    'loggedout'  => 'Вы вышли из системы!',
];

Мы все flash-сообщения показываем в layout-шаблоне, так что изменим в методах registered(), authenticated() и loggedout() ключ status на ключ success. И изменим значение переменной redirectTo, чтобы после регистрации, входа и выхода пользователи перенаправлялись на главную страницу.

class RegisterController extends Controller {
    /**
     * Сразу после регистрации выполняем редирект на главную страницу сайта
     */
    protected $redirectTo = '/';

    /* ... */

    /**
     * Сразу после регистрации выполняем редирект и устанавливаем flash-сообщение
     */
    protected function registered(Request $request, $user) {
        return redirect($this->redirectTo)
            ->with('success', trans('auth.success'));
    }
}
class LoginController extends Controller {
    /**
     * Куда выполнять редирект после входа в систему и после выхода из системы
     */
    protected $redirectTo = '/';

    /* ... */

    /**
     * Сразу после входа выполняем редирект и устанавливаем flash-сообщение
     */
    protected function authenticated(Request $request, $user) {
        return redirect($this->redirectTo)
            ->with('success', __('You are logged in!'));
    }

    /**
     * Сразу после выхода выполняем редирект и устанавливаем flash-сообщение
     */
    protected function loggedOut(Request $request) {
        return redirect($this->redirectTo)
            ->with('success', trans('auth.loggedout'));
    }
}

И нам еще осталось стилизовать страницы регистрации, аутентификации и восстановления пароля, чтобы шапка сайта была как на всех прочих страницах. Для этого достаточно изменить директиву @extends() и убрать div с css-классом container (контейнер у нас уже есть в layout-шаблоне).

@extends('layouts.site')

@section('content')
<div class="row justify-content-center">
    <div class="col-md-8">
    <!-- здесь форма регистрации -->
    </div>
</div>
@endsection

@extends('layouts.site')

@section('content')
<div class="row justify-content-center">
    <div class="col-md-8">
    <!-- здесь форма аутентификации -->
    </div>
</div>
@endsection
@extends('layouts.site')

@section('content')
<div class="row justify-content-center">
    <div class="col-md-8">
    <!-- здесь форма ввода адреса почты (восстановление пароля) -->
    </div>
</div>
@endsection
@extends('layouts.site')

@section('content')
<div class="row justify-content-center">
    <div class="col-md-8">
    <!-- здесь форма ввода нового пароля (восстановление пароля) -->
    </div>
</div>
@endsection

Теперь можно удалить контроллер HomeController, layout-шаблон app.blade.php и шаблон home.blade.php.

Поиск: Laravel • Web-разработка • Блог • Практика • Форма • Фреймворк • Аутентификация • База данных • MySQL • PHP • Пользователь

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.