Часто новички изучающие Laravel и VueJS в качестве frontend фреймворка, задаются вопросами:
- Как сделать ajax запрос?
- Как зарегистрировать пользователя?
- Как залогинить пользователя?
Сегодня мы напишем простую систему регистрации и логина для нашего приложения и на небольшом примере найдем ответы на вопросы.
В предыдущей статье мы настроили окружение и установили базовые компоненты, Laravel
и Vue
. Сейчас будем заниматься страницей регистрации и логина.
Шаг №1. Подготовка.
Мы не будем использовать пакеты для начального создания регистрации/логина, такие как laravel/ui, Jetstream или laravel/breeze.
Эти пакеты дают нам ненужный набор компонентов, которые мы использовать не будем. Поэтому регистрацию и логин напишем с чистого листа. Таким образом еще и научимся создавать свою «кастомную» регистрацию/логин.
Если вы используете docker в качестве среды разработки, все команды(кроме npm) нужно выполнять внутри контейнера. Для того чтобы попасть в терминал контейнера выполните команду docker exec -it finances-php bash
Нам нужно создать два контроллера, один для регистрации, второй для логина.
php artisan make:controller Auth/LoginController php artisan make:controller Auth/RegistrationController
Теперь добавим маршруты в файл routes/api.php.
<?php use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | is assigned the "api" middleware group. Enjoy building your API! | */ Route::post('login', [\App\Http\Controllers\Auth\LoginController::class, 'login']); Route::post('registration', [\App\Http\Controllers\Auth\RegistrationController::class, 'registration']);
Важно!
Этим мы только создали файлы контроллеров и прописали в маршрутах обработчики. Код сейчас работать не будет, работа с методами контроллеров находится в Шаге 4 и Шаге 5
Шаг №2. Основной контроллер, шаблон, настройка маршрутов.
Шаблоны нам не нужны, мы будем использовать vue в качестве фронтенд фреймворка, поэтому можем смело удалить все файлы в папке resources/views и оставим только один, home.blade.php.
resources/views/home.blade.php:
<html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title></title> <link rel="stylesheet" href="{{mix('css/app.css')}}"> </head> <body> <div id="app"></div> <script src="{{mix('js/app.js')}}"></script> </body> </html>
routes/web.php
<?php use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/{any}', [App\Http\Controllers\HomeController::class, 'index']) ->where('any', '^(?!api).*$');
Мы будем использовать для роутинга vue-router, другими словами, весь клиентский роутинг будет на стороне vue(браузера). Поэтому нам нужна конструкция, которая все маршруты будет отправлять в HomeController, кроме маршрутов /api.
app/Http/Controllers/HomeController.php
<?php namespace App\Http\Controllers; /** * Class HomeController * @package App\Http\Controllers */ class HomeController extends Controller { /** * Show the application dashboard. * * @return \Illuminate\Contracts\Support\Renderable */ public function index() { return view('home'); } }
Шаг №3. Vue роутинг, шаблон страницы логина/регистрации.
Создадим файл в котором будем отображать все компоненты(страницы) нашего приложения, выполнять роутинг и прочие базовые вещи, нужные для работы фронта.
resources/js/api/auth.js
export const API_LOGIN_URL = '/api/login'; export const API_REGISTRATION_URL = '/api/registration';
В файлах из директории resources/js/api мы будем хранить константы с нашими api маршрутами.
resources/js/pages/Login.vue
<template> <div> <span v-if="loggedIn">Successfully logged in</span><br> <input type="text" v-model="form.email" placeholder="Email"><br> <input type="password" v-model="form.password" placeholder="Password"><br> <button @click="sendForm" :disabled="pending">Login</button> </div> </template> <script> import axios from 'axios'; import { API_LOGIN_URL } from '../api/auth'; export default { name: 'Login', data() { return { pending: false, loggedIn: false, form: { email: null, password: null } } }, methods: { sendForm() { if (this.pending === false) { this.pending = true; axios.post(API_LOGIN_URL, this.form) .then(response => { this.loggedIn = true; }) .catch(errors => {}) .then(() => { this.pending = false; }); } } } } </script> <style scoped> </style>
Это очень примитивный пример, для того чтобы показать примерную логику работы. В дальнейшем мы стилизуем этот компонент, добавим валидацию и редирект при успешной авторизации.
Здесь у нас для выполнения AJAX запроса к нашему backend приложению мы используем библиотеку axios. pending мы используем для того чтобы при множественных нажатиях на кнопку не слать много запросов пока предыдущий еще не завершится. Аналогично будет и на странице регистрации.
resources/js/pages/Registration.vue
<template> <div> <span v-if="registered">Successfully registered</span><br> <input type="text" v-model="form.name" placeholder="Name"><br> <input type="text" v-model="form.email" placeholder="Email"><br> <input type="password" v-model="form.password" placeholder="Password"><br> <input type="password" v-model="form.password_confirmation" placeholder="Password Confirmation"><br> <button @click="sendForm" :disabled="pending">Registration</button> </div> </template> <script> import axios from 'axios'; import {API_REGISTRATION_URL} from '../api/auth'; export default { name: 'Registration', data() { return { pending: false, registered: false, form: { name: null, email: null, password: null, password_confirmation: null, } } }, methods: { sendForm() { if (this.pending === false) { this.pending = true; axios.post(API_REGISTRATION_URL, this.form) .then(response => { this.registered = true; }) .catch(errors => {}) .then(() => { this.pending = false; }); } } } } </script>
Для регистрации сделали тоже довольно примитивный компонент в котором будет только отправка формы.
Теперь нам нужно собрать всё в единое целое и чтобы работало. Начинаем подключать маршрутизатор.
Для того чтобы установить vue-router, нужно выполнить команду npm install vue-router
. (Полное руководство https://router.vuejs.org/ru/installation.html).
Создадим файл resources/js/router/index.js, в нем будем описывать маршруты приложения.
resources/js/router/index.js
import Vue from 'vue'; import VueRouter from 'vue-router'; import Login from '../pages/Login'; import Registration from '../pages/Registration'; Vue.use(VueRouter); const routes = [ { path: '/', }, { path: '/login', component: Login }, { path: '/registration', component: Registration } ]; const router = new VueRouter({ mode: 'history', routes }); export default router;
resources/js/components/App.vue
<template> <div> <router-link to="/login">Login</router-link> <router-link to="/registration">Registration</router-link> <router-view></router-view> </div> </template> <script> export default { name: 'App' } </script> <style scoped> </style>
Теперь все готово, осталось только отредактировать файл resources/js/app.js и можно запускать билд нашего приложения.
/** * First we will load all of this project's JavaScript dependencies which * includes Vue and other libraries. It is a great starting point when * building robust, powerful web applications using Vue and Laravel. */ import App from './components/App'; import router from './router'; window.Vue = require('vue'); /** * The following block of code may be used to automatically register your * Vue components. It will recursively scan this directory for the Vue * components and automatically register them with their "basename". * * Eg. ./components/ExampleComponent.vue -> <example-component></example-component> */ // const files = require.context('./', true, /\.vue$/i) // files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default)) /** * Next, we will create a fresh Vue application instance and attach it to * the page. Then, you may begin adding components to this application * or customize the JavaScript scaffolding to fit your unique needs. */ const app = new Vue({ el: '#app', router, render: h => h(App) });
Для «компиляции» vue в понятный браузеру js у нас уже есть несколько команд:
- npm run dev — быстрый билд без минификаций и оптимизаций
- npm run prod — готовый для продакшена билд
- npm run watch — запущен постоянно и при изменении файлов, автоматически их билдит, удобно при разработке
Для локальной разработки нам достаточно держать постоянно запущенным npm run watch.
Если вы все сделали правильно, открыв страницу http://localhost, вы должны увидеть слева вверху 2 ссылки на логин и регистрацию, а по клике по ним 2 разные формы.
Шаг №4. Регистрация
Первым делом создадим класс (ссылка на документацию) для валидации входящих данных при регистрации, делается это командой:
php artisan make:request RegisterUserRequest
Откроем файл, который был создан командой выше app/Http/Requests/RegisterUserRequest.php
и добавим правила валидации (не забудьте удалить метод authorize
).
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; /** * Class RegisterUserRequest * @package App\Http\Requests */ class RegisterUserRequest extends FormRequest { /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'name' => 'required', 'email' => 'required|email|unique:users', 'password' => 'required|confirmed', ]; }
Добавили правила для полей name
, email
, password
(список всех правил).
Редактируем контроллер
<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Http\Requests\RegisterUserRequest; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Log; /** * Class RegistrationController * @package App\Http\Controllers\Auth */ class RegistrationController extends Controller { /** * @param RegisterUserRequest $request * @return \Illuminate\Http\JsonResponse */ public function registration(RegisterUserRequest $request) { try { User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => Hash::make($request->password) ]); return response()->json([], Response::HTTP_OK); } catch (\Exception $e) { Log::error($e->getMessage(), ['trace' => $e->getTraceAsString()]); return response()->json([], Response::HTTP_INTERNAL_SERVER_ERROR); } } }
Прописываем аргументом метода registration
наш новый класс RegisterUserRequest
.
Далее мы формируем массив с данными для передачи их в метод создания модели. При формировании массива, для пароля генерируем хеш фасадом Hash
, метод make
.
На данном этапе кода контроллера будет достаточно, даже с пустым ответом, мы будем ориентироваться на код ответа, а не на тело.
Шаг №5. Аутентификация (Логин)
Поскольку мы используем SPA и REST api, использовать механизм сессий не получится. Для того, чтобы backend(laravel) мог понять какой пользователь залогене, есть простой в использовании пакет laravel/sanctum
(Документация).
Устанавливаем пакет командой:
composer require laravel/sanctum
После установки, добавляем в проект файлы конфигурации:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
Для подключения Sanctum
к проекту, нужно добавить middleware
ко всем маршрутам api
. Откроем файл app/Http/Kernel.php
. В файле находим свойство protected $middlewareGroups
и к массиву с ключом api
добавляем в самое начало строку \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class
.
/** * The application's route middleware groups. * * @var array */ protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];
Последним и завершающим шагом в предварительной настройке пакета, это добавление трейта для модели пользователя.
Откроем файл app/Models/User.php
и добавим трейт HasApiTokens
:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; /** * Class User * @package App\Models */ class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable;
Не забудьте добавить в начале файла после объявления namespace
— use Laravel\Sanctum\HasApiTokens;
Создадим FormRequest для запроса на логин, чтобы провалидировать входящие данные:
php artisan make:request LoginUserRequest
app/Http/Requests/LoginUserRequest.php
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; /** * Class LoginUserRequest * @package App\Http\Requests */ class LoginUserRequest extends FormRequest { /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'email' => 'required|email', 'password' => 'required', ]; } }
app/Http/Controllers/Auth/LoginController.php
<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Http\Requests\LoginUserRequest; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Auth; /** * Class LoginController * @package App\Http\Controllers\Auth */ class LoginController extends Controller { /** * @param LoginUserRequest $request * @return \Illuminate\Http\JsonResponse */ public function login(LoginUserRequest $request) { if (Auth::attempt($request->validated())) { $token = Auth::user()->createToken('api'); return response()->json([ 'token' => $token->plainTextToken ]); } return response()->json([], Response::HTTP_UNAUTHORIZED); } }
Auth::attempt
— метод попробует залогинить пользователя. Сходит в базу, поищет там пользователя, сравнит пароли и выдаст результат, успешно или нет.$request->validated()
— передаст только те поля, которые прописаны в правилах валидации.Auth::user()
— возвращает нам объект залогиненого пользователя.createToken('api')
— сгенерирует объект токена для пользователя. Аргументом мы передаем имя токена, может быть на ваше усмотрение, полезно когда пользователь заходит на сайт с разных источников, например с веб интерфейса, мобильного приложения или через десктопное.$token->plainTextToken
— берет строковое значение токена.
Шаг №6. Запускаем приложение.
- Проверяем есть ли в корневой директории файл
.env
(если нет, создаем и копируем все из файла примера —.env.example
) - Проверяем значение
APP_KEY
(если пустое, запускаем командуphp artisan key:generate
) - Добавляем логин, пароль и имя базы данных для подключения к базе данных. (В предыдущем уроке я использовал для окружения докер, напишу конфигурацию под него)
DB_HOST
—database
(Поскольку я запускаю в докере, в качестве хоста нужно писать имя сервиса изdoker-compose.yml
файла)DB_DATABASE
—finances
DB_USERNAME
—root
DB_PASSWORD
—secret
- Запускаем миграции командой
php artisan migrate
- Запускаем сборку фронта командой
npm run dev
После подготовительных этапов открываем сайт (у меня это адрес http://localhost
), переходим на страницу регистрации, заполняем поля и жмем кнопку Registration
. Если вы все сделали правильно, у вас должна появится такая страница с сообщением об успешной регистрации:

Переходим на страницу Login
и вводим данные которые использовали при регистрации. Если увидели сообщение об успешном логине, значит все работает.

Довольно большая статья получилась, надеюсь я все правильно и доступно написал. В следующей статье мы с вами найдем и добавим шаблон для главной страницы/регистрации/логина, хранилище VueX
.
Если у вас появились вопросы, вы заметили неточности или у вас появились ошибки, пишите комментарии будем разбираться.
Довольно познавательно! Как раз начал вариться в этой теме.
Пока разделяю бек на блейдах делаю а фронт хочу на vue но возникают свои проблемы с роутингом.
Буду следить за обновлениями
Здравствуйте! Жду продолжения статей, получилось интересно!