В 20-му уроку мы подошли к завершающему этапу нашего курса по Laravel. Сегодня мы создадим полноценный блог с нуля, используя все знания, которые вы получили за предыдущие 19 уроков. В этом уроке мы реализуем CRUD для статей, систему комментариев с возможностью ответов, поиск через Laravel Scout и задеплоим проект в интернет.
Реализация CRUD для статей
CRUD (Create, Read, Update, Delete) основа большинства веб-приложений. Начнем с создания модели, миграции и контроллера для статей.
1. Создание модели и миграции
Откройте терминал и выполните:
php artisan make:model Article -mcr
Эта команда создаст модель Article
, контроллер ArticleController
и миграцию.
Отредактируем миграцию create_articles_table
:
public function up() { Schema::create('articles', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('content'); $table->foreignId('user_id')->constrained(); $table->timestamps(); }); }
Выполните миграцию:
php artisan migrate
2. Настройка отношений в модели
В модели Article
добавим связь с пользователем:
class Article extends Model { protected $fillable = ['title', 'content', 'user_id']; public function user() { return $this->belongsTo(User::class); } }
3. Создание маршрутов
Добавим в routes/web.php
:
Route::resource('articles', ArticleController::class)->middleware('auth');
4. Реализация методов контроллера
В ArticleController
реализуем основные методы:
Создание статьи (create, store):
public function create() { return view('articles.create'); } public function store(Request $request) { $validated = $request->validate([ 'title' => 'required|max:255', 'content' => 'required' ]); auth()->user()->articles()->create($validated); return redirect()->route('articles.index'); }
Отображение статей (index, show):
public function index() { $articles = Article::latest()->paginate(10); return view('articles.index', compact('articles')); } public function show(Article $article) { return view('articles.show', compact('article')); }
Редактирование (edit, update) и удаление (destroy):
public function edit(Article $article) { $this->authorize('update', $article); return view('articles.edit', compact('article')); } public function update(Request $request, Article $article) { $this->authorize('update', $article); $validated = $request->validate([...]); // аналогично store $article->update($validated); return redirect()->route('articles.show', $article); } public function destroy(Article $article) { $this->authorize('delete', $article); $article->delete(); return redirect()->route('articles.index'); }
5. Создание Blade-шаблонов
Пример для resources/views/articles/create.blade.php
:
<form action="{{ route('articles.store') }}" method="POST"> @csrf <input type="text" name="title" placeholder="Заголовок"> <textarea name="content" placeholder="Текст статьи"></textarea> <button type="submit">Опубликовать</button> </form>
Практическая задача: добавьте валидацию для поля content
(минимум 100 символов) и кастомное сообщение об ошибке.
Система комментариев с деревом ответов
Реализуем вложенные комментарии с использованием отношения parent_id
.
1. Модель и миграция для комментариев
Создайте модель и миграцию:
php artisan make:model Comment -m
В миграции create_comments_table
:
public function up() { Schema::create('comments', function (Blueprint $table) { $table->id(); $table->text('body'); $table->foreignId('user_id')->constrained(); $table->foreignId('article_id')->constrained(); $table->foreignId('parent_id')->nullable()->constrained('comments'); $table->timestamps(); }); }
В модели Comment
добавим отношения:
class Comment extends Model { public function article() { return $this->belongsTo(Article::class); } public function user() { return $this->belongsTo(User::class); } public function replies() { return $this->hasMany(Comment::class, 'parent_id'); } }
2. Добавление комментариев
Создадим контроллер CommentController
:
public function store(Request $request, Article $article) { $request->validate(['body' => 'required|min:3']); $comment = new Comment([ 'body' => $request->body, 'user_id' => auth()->id(), 'parent_id' => $request->parent_id ]); $article->comments()->save($comment); return back(); }
3. Отображение вложенных комментариев
В шаблоне articles/show.blade.php
:
@foreach ($article->comments->whereNull('parent_id') as $comment) @include('comments._item', ['comment' => $comment]) @endforeach
Файл resources/views/comments/_item.blade.php
:
<div class="comment"> <p>{{ $comment->body }}</p> <form action="{{ route('comments.store', $article) }}" method="POST"> @csrf <input type="hidden" name="parent_id" value="{{ $comment->id }}"> <textarea name="body" placeholder="Ответить..."></textarea> <button type="submit">Ответить</button> </form> @foreach ($comment->replies as $reply) @include('comments._item', ['comment' => $reply]) @endforeach </div>
Практическая задача: ограничьте глубину вложенности комментариев до 3 уровней. Подсказка: используйте рекурсивный счетчик в Blade.
Поиск по статьям через Scout
Laravel Scout предоставляет простой способ реализации полнотекстового поиска.
1. Установка Scout и драйвера
Установите пакеты:
composer require laravel/scout composer require laravel/scout-database-driver
В .env
добавьте:
SCOUT_DRIVER=database
2. Настройка модели Article
В модели Article
подключите трейт Searchable
:
use Laravel\Scout\Searchable; class Article extends Model { use Searchable; public function toSearchableArray() { return [ 'title' => $this->title, 'content' => $this->content ]; } }
3. Реализация поиска
Добавьте маршрут:
Route::get('/search', [ArticleController::class, 'search']);
В контроллере:
public function search(Request $request) { $query = $request->input('q'); $articles = Article::search($query)->paginate(10); return view('articles.search', compact('articles', 'query')); }
В шаблоне search.blade.php
:
<form action="{{ route('articles.search') }}"> <input type="text" name="q" value="{{ $query }}"> <button>Найти</button> </form> @foreach ($articles as $article) <!-- Вывод результатов --> @endforeach
Практическая задача: добавьте поиск по комментариям. Подсказка: создайте отдельный индекс для модели Comment
.
Деплой проекта
Развернем приложение на shared-хостинге. Для примера используем Hostinger.
1. Подготовка к деплою
- Убедитесь, что
APP_ENV=production
в.env
. - Выполните
php artisan config:cache
иphp artisan route:cache
.
2. Перенос файлов на сервер
Скопируйте файлы через FTP или Git. Если используете Git:
git remote add production username@server:/path/to/project git push production master
3. Настройка базы данных
Создайте БД через панель управления хостингом и обновите настройки в .env
:
DB_CONNECTION=mysql DB_HOST=... DB_PORT=... DB_DATABASE=... DB_USERNAME=... DB_PASSWORD=...
4. Запуск миграций
На сервере выполните:
php artisan migrate --force
Практическая задача: настройте автоматический деплой через GitHub Actions. Подсказка: используйте секреты для хранения учетных данных.
Подведение итогов
Поздравляю! Вы только что создали полноценный блог на Laravel. Давайте вспомним, что мы покрыли:
- CRUD для статей с авторизацией и политиками.
- Вложенные комментарии с использованием рекурсивных отношений.
- Полнотекстовый поиск через Laravel Scout.
- Деплой проекта на реальный сервер.
Этот проект, отличное портфолио для старта вашей карьеры. Не останавливайтесь на достигнутом, попробуйте добавить теги, лайки, подписки на статьи.
Полный курс с уроками по Laravel для начинающих по ссылке: https://max-gabov.ru/laravel-dlya-nachinaushih