Мини-блог на Laravel, часть 3. Постраничная навигация, layout-шаблон и поиск по блогу
12.09.2020
Теги: CLI • Laravel • MySQL • PHP • route • Web-разработка • БазаДанных • Блог • Класс • Практика • Фреймворк • Шаблонизатор • ШаблонСайта
Методы контроллера
Наш контроллер умеет только выводить список всех постов блога. Но нам нужно, чтобы контроллер умел еще показывать отдельный пост, добавлять новый, редактировать или удалять существующий. Поэтому удаляем файл контроллера и создаем заново с помощью artisan-команды
> php artisan make:controller PostController -r
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class PostController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { // ..... } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { // ..... } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { // ..... } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { // ..... } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit($id) { // ..... } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { // ..... } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { // ..... } }
Изменим метод index()
, чтобы получать все посты блога с сортировкой по дате и с пагиницией:
class PostController extends Controller { /* ... */ public function index() { $posts = Post::select('posts.*', 'users.name as author') ->join('users', 'posts.author_id', '=', 'users.id') ->orderBy('posts.created_at', 'desc') ->paginate(4); return view('posts.index', compact('posts')); } /* ... */ }
И внесем изменения в шаблон, чтобы показывать картинку, автора поста и дату публикации:
<!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> <link rel="stylesheet" href="{{ asset('css/app.css') }}"> </head> <body> <div class="container"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <!-- ... --> </nav> <h1 class="mt-2 mb-3">Все посты блога</h1> <div class="row"> @foreach ($posts as $post) <div class="col-6 mb-4"> <div class="card"> <div class="card-header"><h3>{{ $post->title }}</h3></div> <div class="card-body"> <img src="{{ $posts->image ?? asset('img/default.jpg') }}" alt="" class="img-fluid"> <p class="mt-3 mb-0">{{ $post->excerpt }}</p> </div> <div class="card-footer"> <div class="clearfix"> <span class="float-left"> Автор: {{ $post->author }} <br> Дата: {{ date_format($post->created_at, 'd.m.Y H:i') }} </span> <a href="#" class="btn btn-dark float-right">Читать дальше</a> </div> </div> </div> </div> @endforeach </div> {{ $posts->links() }} </div> </body> </html>
Если для поста не загружена картинка, тогда показываем картинку по умолчанию default.jpg
, которую разместим в директории img
в корне веб-сервера.
Layout-шаблон
Сейчас у нас всего один шаблон, но скоро их будет много. У всех шаблонов будет много общего, так что создадим общий шаблон и будем его наследовать. Создаем файл site.blade.php
в директории resources/views/layouts
:
<!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> <link rel="stylesheet" href="{{ asset('css/app.css') }}"> </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="#">Создать</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Контакты</a> </li> </ul> <form class="form-inline my-2 my-lg-0" action=""> <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> </div> </nav> @yield('content') </div> </body> </html>
А шаблон index.blade.php
в директории resources/views/posts
будет наследовать site.blade.php
:
@extends('layouts.site') @section('content') <h1 class="mt-2 mb-3">Все посты блога</h1> <div class="row"> @foreach ($posts as $post) <div class="col-6 mb-4"> <div class="card"> <div class="card-header"><h3>{{ $post->title }}</h3></div> <div class="card-body"> <img src="{{ $posts->image ?? asset('img/default.jpg') }}" alt="" class="img-fluid"> <p class="mt-3 mb-0">{{ $post->excerpt }}</p> </div> <div class="card-footer"> <div class="clearfix"> <span class="float-left"> Автор: {{ $post->author }} <br> Дата: {{ date_format($post->created_at, 'd.m.Y H:i') }} </span> <a href="#" class="btn btn-dark float-right">Читать дальше</a> </div> </div> </div> </div> @endforeach </div> {{ $posts->links() }} @endsection
Поиск по блогу
Добавим еще один метод search()
в класс контроллера, который будет искать посты блога по введенной фразе:
class PostController extends Controller { /* ... */ public function search(Request $request) { $search = $request->input('search', ''); // образаем слишком длинный запрос $search = iconv_substr($search, 0, 64); // удаляем все, кроме букв и цифр $search = preg_replace('#[^0-9a-zA-ZА-Яа-яёЁ]#u', ' ', $search); // сжимаем двойные пробелы $search = preg_replace('#\s+#u', ' ', $search); if (empty($search)) { return view('posts.search'); } $posts = Post::select('posts.*', 'users.name as author') ->join('users', 'posts.author_id', '=', 'users.id') ->where('posts.title', 'like', '%'.$search.'%') // поиск по заголовку поста ->orWhere('posts.body', 'like', '%'.$search.'%') // поиск по тексту поста ->orWhere('users.name', 'like', '%'.$search.'%') // поиск по автору поста ->orderBy('posts.created_at', 'desc') ->paginate(4) ->appends(['search' => $request->input('search')]);; return view('posts.search', compact('posts')); } /* ... */ }
Добавим новый маршрут в файл routes/web.php
, а заодно — зададим имена для маршрутов, чтобы с ними было удобно работать:
<?php use Illuminate\Support\Facades\Route; Route::get('/', 'PostController@index')->name('blog.index'); Route::get('post/index', 'PostController@index')->name('post.index'); Route::get('post/search', 'PostController@search')->name('post.search');
В layout-шаблоне site.blade.php
установим значение атрибута action
тега form
:
<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>
И осталось только создать шаблон search.blade.php
в директории resources/views/posts
:
@extends('layouts.site') @section('content') <h1 class="mt-2 mb-3">Результаты поиска</h1> @if (isset($posts) && count($posts)) <div class="row"> @foreach ($posts as $post) <div class="col-6 mb-4"> <div class="card"> <div class="card-header"><h3>{{ $post->title }}</h3></div> <div class="card-body"> <img src="{{ $posts->image ?? asset('img/default.jpg') }}" alt="" class="img-fluid"> <p class="mt-3 mb-0">{{ $post->excerpt }}</p> </div> <div class="card-footer"> <div class="clearfix"> <span class="float-left"> Автор: {{ $post->author }} <br> Дата: {{ date_format($post->created_at, 'd.m.Y H:i') }} </span> <a href="#" class="btn btn-dark float-right">Читать дальше</a> </div> </div> </div> </div> @endforeach </div> {{ $posts->links() }} @else <p>По вашему запросу ничего не найдено</p> @endif @endsection
- Мини-блог на Laravel, часть 4. Создание нового поста, загрузка и обрезка изображения
- Мини-блог на Laravel, часть 2. Создание контроллера, представления и маршрута
- Мини-блог на Laravel, часть 5. Просмотр и редактирование отдельного поста блога
- Мини-блог на Laravel, часть 1. Создание таблиц БД, наполнение тестовыми данными
- Блог на Laravel 7, часть 12. Доп.страницы сайта в панели управления и в публичной части
- Блог на Laravel 7, часть 11. Панель управления — назначение ролей и прав для пользователей
- Блог на Laravel 7, часть 10. Личный кабинет — CRUD-операции над постами и комментариями
Поиск: CLI • Laravel • MySQL • PHP • Web-разработка • База данных • Класс • Практика • Фреймворк • Шаблонизатор • Шаблон сайта • Блог • route • Маршрут