У сучасних веб-додатках часто виникає потреба збирати дані користувачів через форми. Якщо форма містить багато полів, вона може бути занадто довгою та незручною, що негативно впливає на досвід користувача та може викликати високий рівень відмов. Простим вирішенням цього є створення форм із багатьма кроками.
У цьому посібнику ми створимо багатокрокову форму, використовуючи MongoDB, Laravel та Laravel Livewire. MongoDB використовується тут, оскільки вона зберігає дані у документній формі з гнучкою схемою, не потребуючи багатьох таблиць та зв'язків, як звичайні реляційні бази даних. Не потрібно створювати міграції. Якщо ви не знайомі з MongoDB, прочитайте наші документи для початківців.
Livewire — це повноцінний фреймворк Laravel, який дозволяє розробникам легко створювати динамічні інтерфейси без потреби в написанні великої кількості JavaScript
коду. Він простий у використанні і використовує синтаксис Laravel. Щоб почати працювати з Livewire, зверніться до документації Laravel. Тож давайте почнемо.
Щоб слідкувати за цим посібником, необхідно:
Перед продовженням перевірте, що у вас встановлений MongoDB PHP Driver, щоб MongoDB працювала ефективно. Перевірте це, запустивши наступну команду:
php -i | grep mongo
Очікуваний вивід має бути подібним до зображення нижче:
Якщо ви отримали інший вихід, ймовірно, у вас не встановлений MongoDB PHP Driver. Щоб його встановити, відвідайте це детальне керівництво.
Також переконайтесь, що у вас встановлені PHP та Composer.
Давайте розпочнемо створення багатокрокової форми, створивши новий проект Laravel. Для цього виконайте наступну команду:
composer create-project laravel/laravel multi_step_form_tutorial
Після створення проекту налаштуємо застосунок для роботи з MongoDB. Laravel не підтримує MongoDB за замовчуванням, тому нам потрібно встановити та налаштувати пакет Laravel-MongoDB. Встановимо Laravel-mongodb за допомогою наступної команди:
composer require mongodb/laravel-mongodb
Після успішної установки потрібні зміни у файлі config/database.php
. Додайте наступний код:
'mongodb' => [
'driver' => 'mongodb',
'dsn' => env('MONGODB_URI'),
'database' => 'YOUR_DATABASE_NAME',
],
Пояснимо: значення dsn
береться з файлу .env. Створіть у цьому файлі значення 'MONGODB_URI'
і встановіть його на ваш рядок підключення MongoDB Atlas:
MONGODB_URI="<<MONGODB_ATLAS_CONNECTION_STRING>>"
DB_CONNECTION=mongodb
Далі встановимо та налаштуємо Livewire для роботи в нашому застосунку. Для початку встановимо Livewire:
composer require livewire/livewire
Після установки потрібно імпортувати Livewire в додаток. Щоб зберегти організацію, створимо файл макета та перетворимо його на Blade компонент. Якщо вам незнайомі шаблони Blade, ознайомтеся з документацією Laravel. Згенеруємо наш Blade шаблон за допомогою команди:
php artisan make:component layouts/App
Це створить два файли: app/View/Components/layouts/App.php
та resources/views/components/layouts/app.blade.php
. Відкрийте resources/views/components/layouts/app.blade.php
та замініть вміст на наступний код:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Багатокрокова форма</title>
@vite(['resources/css/app.css', 'resources/js/app.js']) {{-- Якщо ви використовуєте Vite --}}
@livewireStyles
</head>
<body class="bg-gray-100">
<div class="container mx-auto mt-10">
{{ $slot ?? '' }}
</div>
@livewireScripts
</body>
</html>
У коді вище ми додали HTML-розмітку для макета. Ми також імпортували Livewire за допомогою @livewireStyles
і @livewireScripts
, щоб Livewire був доступний на усіх сторінках з цим макетом.
Перед створенням Livewire компонента для багатокрокової форми перевіримо, чи налаштована база даних правильно. Для цього створимо маршрут у routes/web.php
, щоб протестувати підключення до нашого кластеру MongoDB Atlas. Відкрийте routes/web.php
та додайте наступний код нижче існуючого:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
Route::get('/ping', function (Request $request) {
$connection = DB::connection('mongodb');
try {
$connection->command(['ping' => 1]);
$msg = 'MongoDB доступний!';
} catch (Exception $e) {
$msg = 'Не вдалося підключитися до MongoDB. Помилка: ' . $e->getMessage();
}
return ['msg' => $msg];
});
Тепер запустіть застосунок:
php artisan serve
Це запустить додаток за IP адресою 127.0.0.1:8000
. Якщо порт 8000
зайнятий, Laravel автоматично переключиться на вільний порт.
В іншому терміналі виконайте наступну команду для установки та збірки Node.js залежностей у додатку:
npm install
Давайте протестуємо. В браузері перейдіть за новим маршрутом 127.0.0.1:8000/ping
. Якщо все зроблено правильно, ваш екран виглядатиме, як показано на зображенні нижче:
Перед створенням Livewire компонента давайте розглянемо, як працюватиме наша форма. Ми створимо багатокрокову форму з трьох (3) кроків. Перший крок міститиме базову інформацію про користувача, таку як ім'я та електронна адреса. У другому кроці будуть дані адреси, такі як вулиця, місто чи штат, а в третьому — варіанти для вибору статі та сімейного стану.
Форма буде перевіряти дані на кожному кроці, зберігати перевірені дані та мати кнопки "Назад", "Далі" та "Надіслати". Інформація зберігатиметься у нашій базі даних після кожного успішного кроку.
Гнучка схема MongoDB ідеально підходить для роботи з динамічними даними. Ми можемо легко додавати нові поля до форми без потреби у змінах структури міграцій. При роботі з MongoDB у Laravel не потрібно створювати міграції — нові поля можна додавати безпосередньо.
Отже, давайте створимо наш Livewire компонент за допомогою команди:
php artisan livewire:make MultiStepForm
Після виконання команди буде створено Livewire компонент з двома файлами: app/Livewire/MultiStepForm.php
та resources/views/livewire/multi-step-form.blade.php
. У app/Livewire/MultiStepForm.php
міститиметься весь PHP
код для компонента, а resources/views/livewire/multi-step-form.blade.php
— HTML.
Давайте імпортуємо наш Livewire компонент у файл resources/views/welcome.blade.php
, оновивши вміст з наступним кодом:
<x-layouts.app>
<livewire:multi-step-form />
</x-layouts.app>
У routes/web.php
створіть маршрут для нашого Livewire компонента, додавши наступний код:
Route::get('/', function () {
return view('welcome');
});
Після цього замініть вміст файлу resources/views/livewire/multi-step-form.blade.php
на наступний:
<div>
<div class="max-w-4xl mx-auto p-6 bg-white shadow-xl rounded-xl">
@if (session('success'))
<div class="mb-4 p-4 bg-green-100 text-green-700 rounded-lg">
{{ session('success') }}
</div>
@endif
<h1 class="text-4xl">Багатокрокова форма з MongoDB</h1>
<h2 class="text-base font-semibold mb-4">Крок {{ $currentStep }} з 3</h2>
<div>
@if ($currentStep == 1)
<div>
<label class="block text-sm">Ім'я</label>
<input type="text" wire:model="name" class="border rounded-lg p-2 w-full">
@error('name') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
<label class="block mt-2 text-sm">Електронна адреса</label>
<input type="email" wire:model="email" class="border rounded-lg p-2 w-full">
@error('email') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
@endif
@if ($currentStep == 2)
<div>
<label class="block text-sm">Адреса</label>
<input type="text" wire:model="address" class="border p-2 w-full">
@error('address') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
<label class="block mt-2">Місто</label>
<input type="text" wire:model="city" class="border p-2 w-full">
@error('city') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
@endif
@if ($currentStep == 3)
<div>
<label class="block">Стать</label>
<select wire:model="gender" class="border p-2 w-full">
<option value="male">Чоловіча</option>
<option value="female">Жіноча</option>
</select>
</div>
@endif
<div class="mt-4 flex justify-between">
@if ($currentStep > 1)
<button wire:click="previousStep" class="px-4 py-2 bg-gray-500 text-white rounded">Назад</button>
@endif
@if ($currentStep < 3)
<button wire:click="nextStep" wire:loading.class="opacity-50" class="px-4 py-2 bg-blue-500 text-white rounded">
<span wire:loading.remove>Далі</span>
<span wire:loading>
Завантаження..
</span>
</button>
@else
<button wire:click="nextStep" wire:loading.class="opacity-50" class="px-4 py-2 bg-green-500 text-white rounded">
<span wire:loading.remove>Зберегти</span>
<span wire:loading>
Завантаження..
</span>
</button>
@endif
</div>
</div>
</div>
</div>
У цьому коді представлено HTML та TailwindCSS для нашої багатокрокової форми. Поля форми використовують wire:model
, що зв'язує їх з елементами, які будуть оголошені в компоненті Livewire. Форма обгорнена в центрований контейнер div
з класами Tailwind для відстані, кольору фону та легкого тіні.
У коді використовуємо змінну $currentStep
для відстежування поточного кроку форми, поки користувач заповнює форму.
@if (session('success'))
<div class="mb-4 p-4 bg-green-100 text-green-700 rounded-lg">
{{ session('success') }}
</div>
@endif
Перевіряємо, чи існує повідомлення про успіх у сесії. Якщо так, воно відображається на зеленому фоні з легким зеленим текстом.
У коді використовуємо умовний оператор і значення $currentStep
, щоб визначити поточний крок і відобразити відповідну інформацію для цього кроку:
@if ($currentStep == 1)
@enderror
Це показує кожен крок залежно від значення $currentStep
. На першому кроці відображаються поля для збору імені та електронної адреси. Значення цих полів зв'язані з $name
та $email
у app/Livewire/MultiStepForm.php
за допомогою wire:model
. Цей самий процес повторюється для другого та третього кроків, а також обробляються помилки за допомогою директиви @error()
.
Ми використовуємо умовний оператор для відображення кнопки "Назад" на другому та третьому кроках:
@if ($currentStep > 1)
<button wire:click="previousStep" class="px-4 py-2 bg-gray-500 text-white rounded">Назад</button>
@endif
Аналогічно ми створюємо кнопку "Далі" для переходу до наступного кроку і кнопку "Зберегти" на фінальному етапі, щоб зберегти дані третього кроку. Щоб додати динаміки, ми реалізовуємо wire:loading
, що показує індикатор завантаження під час виконання дій.
PHP драйвер MongoDB значно полегшує роботу з MongoDB у Laravel. Він дозволяє взаємодіяти з MongoDB за допомогою Eloquent так само, як у реляційних базах даних. Щоб розпочати, давайте створимо model
форми:
php artisan make:model MultiStepForm
Ця команда створить файл app\Models\MultiStepForm.php
. Відкрийте файл і оновіть вміст:
<?php
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;
class MultiStepForm extends Model
{
protected $connection = 'mongodb';
protected $table = 'multi_step_form';
protected $fillable = [
'name', 'email', 'address', 'city', 'gender',
];
}
Це стандартний клас моделі Laravel. Ми імпортуємо MongoDB Eloquent, використовуючи use MongoDB\Laravel\Eloquent\Model
. Наша модель підключена до MongoDB за допомогою protected $connection = 'mongodb'
та створює таблицю бази даних з назвою multi_step_form за допомогою protected $table = 'multi_step_form'
.
Тепер давайте завершимо функціональність нашої форми за допомогою Livewire.
Відкрийте app/Livewire/MultiStepForm.php
та замініть вміст на наступне:
<?php
namespace App\Livewire;
use Livewire\Component;
use Illuminate\Support\Facades\DB;
use App\Models\MultiStepForm as MultiStepFormDB;
class MultiStepForm extends Component
{
public $currentStep = 1;
public $totalSteps = 3;
public $name;
public $email;
public $address;
public $city;
public $gender;
public function nextStep()
{
$this->validateStep();
if($this->currentStep < $this->totalSteps){
$this->currentStep++;
}
}
public function previousStep()
{
$this->currentStep--;
}
public function validateStep()
{
if($this->currentStep === 1){
$rules = [
'name' => 'required|string',
'email' => 'required|email',
];
$this->validate($rules);
$this->saveProgress($this->email, ['name' => $this->name], ['email' => $this->email]);
session()->flash('success', 'Крок '.$this->currentStep.' збережено.');
} elseif($this->currentStep === 2) {
$rules = [
'address' => 'required|string',
'city' => 'required|string',
];
$this->validate($rules);
$this->saveProgress($this->email, ['address' => $this->address], ['city' => $this->city]);
session()->flash('success', 'Крок '.$this->currentStep.' збережено.');
} elseif($this->currentStep === 3){
$rules = [
'gender' => 'required|string',
];
$this->validate($rules);
$this->saveProgress($this->email, ['gender' => $this->gender]);
session()->flash('success', 'Крок '.$this->currentStep.' збережено.');
}
}
public function saveProgress($email, ...$formFields)
{
$data = [];
foreach($formFields as $value){
if (is_array($formFields)) {
$data = array_merge($data, $value);
}
}
try {
MultiStepFormDB::updateOrCreate(['email' => $email], $data);
} catch(\Exception $e) {
\Log::error('Помилка збереження MultiStepForm: ' . $e->getMessage());
session()->flash('error', 'Виникла помилка під час збереження ваших даних. Спробуйте ще раз.');
}
}
public function render()
{
return view('livewire.multi-step-form');
}
}
Логіка форми, яку ми створили в resources/views/livewire/multi-step-form.blade.php
, реалізована в app\Models\MultiStepForm.php
. Розглянемо код для кращого розуміння.
З метою відстеження кроків, ми проголосили дві змінні: $currentStep
, яка відслідковує поточний крок форми і має значення 1 за замовчуванням, та $totalSteps
, що вказує загальну кількість кроків — у нашому випадку три.
Ми також проголошуємо інші публічні властивості для зберігання даних форм, таких як name
, email
, address
, city
і gender
. Ці властивості Livewire автоматично оновлюються в реальному часі, коли користувач вводить дані, завдяки wire:model
.
nextStep()
: Цей метод виконує валідацію вводу, викликаючи $this->validateStep()
. Якщо все правильно, користувача переводять на наступний крок і виводять повідомлення про успіх.
previousStep()
: Цей метод зменшує значення $this->currentStep
, повертаючись один крок назад.
$this->validateStep()
: Цей метод відповідає за валідацію та обробку даних форми після кожного кроку, щоб уникнути втрати даних. Він перевіряє, який крок активний, валідує необхідні дані та оновлює базу даних. У першому кроці перевіряють поля name
і email
; у другому — address
та city
; у третьому — gender
. Якщо є помилка, вона виводиться у вигляді повідомлення.
При переході на наступний крок дані зберігаються за допомогою $this->saveProgress()
, який створений для створення чи оновлення даних у нашій базі MongoDB та задля запобігання повторення коду:
public function saveProgress($email, ...$formFields)
{
$data = [];
foreach($formFields as $value){
if (is_array($formFields)) {
$data = array_merge($data, $value);
}
}
try{
MultiStepFormDB::updateOrCreate(['email' => $email], $data);
}catch(\Exception $e){
\Log::error('Помилка збереження MultiStepForm: ' . $e->getMessage());
session()->flash('error', 'Виникла помилка під час збереження ваших даних. Спробуйте ще раз.');
}
}
Цей метод приймає $email
користувача як унікальний ідентифікатор для оновлення існуючої записи або створення нового. Також приймає ...$formFields
, що дозволяє передавати необмежену кількість масивів. У циклі ми збираємо правильні значення у $data
, яке потім передається до MultiStepFormDB::updateOrCreate()
для створення запису.
Метод render()
завантажує Blade вигляд livewire.multi-step-form
, що містить форму.
На цьому етапі ми можемо протестувати наш додаток. Переконайтеся, що ваш Laravel проект все ще запущений, а npm працює. Якщо ні, ви можете запустити його знову за допомогою:
php artisan serve
В іншому терміналі запустіть:
npm run dev
Перейдіть до локального URL, у нашому випадку це http://127.0.0.1:8000/
. Ви повинні побачити екран, подібний до мого:
Заповніть форму та перевірте, щоб усе працювало, як очікується.
Чудово, якщо ви дійшли до цього місця. Ми завершили наш посібник. Давайте підсумуємо, що ми зробили:
Якщо хочете додати собі виклик, спробуйте інтегрувати користувацьку аутентифікацію до форми та налаштувати сповіщення на електронну пошту за допомогою тригерів MongoDB для незавершених форм.
Знайдіть проект на GitHub. Вільно клоніть його, реєструйтесь на MongoDB Atlas та налаштовуйте його під свої потреби. Для додаткової підтримки приєднуйтесь до спільноти розробників MongoDB.