Мини-блог на Laravel, часть 5. Просмотр и редактирование отдельного поста блога
16.09.2020
Теги: Laravel • MySQL • PHP • route • Web-разработка • БазаДанных • Блог • Практика • Фреймворк • ШаблонСайта
Хорошо, с добавлением нового поста вроде разобрались, теперь нужно его показать. Для этого создаем новый шаблон show.blade.php
, добавляем новый маршрут, реализуем метод show()
контроллера. В шаблонах index.blade.php
и search.blade.php
делаем ссылку для просмотра отдельного поста.
Шаблон, маршрут, метод контроллера
Новый шаблон
@extends('layouts.site') @section('content') <div class="row"> <div class="col-12"> <div class="card mt-4 mb-4"> <div class="card-header"> <h1>{{ $post->title }}</h1> </div> <div class="card-body"> <img src="{{ $post->image ?? asset('img/default.jpg') }}" alt="" class="img-fluid"> <p class="mt-3 mb-0">{{ $post->body }}</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> </div> @endsection
Новый маршрут
<?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'); Route::get('post/create', 'PostController@create')->name('post.create'); Route::post('post/store', 'PostController@store')->name('post.store'); Route::get('post/show/{id}', 'PostController@show')->name('post.show');
Метод show()
<?php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; use Intervention\Image\Facades\Image; class PostController extends Controller { /* ... */ public function show($id) { $post = Post::select('posts.*', 'users.name as author') ->join('users', 'posts.author_id', '=', 'users.id') ->find($id); return view('posts.show', compact('post')); } /* ... */ }
Добавляем ссылки для просмотра постов в шаблонах index.blade.php
и search.blade.php
<a href="{{ route('post.show', ['id' => $post->post_id]) }}" class="btn btn-dark float-right">Читать дальше</a>
Вроде все готово, можно проверять. Но тут у меня появилась ошибка:
Illuminate\Database\QueryException SQLSTATE[42S22]: Column not found: 1054 Unknown column 'posts.id' in 'where clause' (SQL: select * from `posts` where `posts`.`id` = 20 limit 1)
Это потому, что по умолчанию Laravel считает, что первичный ключ таблицы должен быть id
, а у нас это post_id
. Давайте сообщим об этом Laravel, для этого отредактируем файл класса модели app/Post.php
.
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $primaryKey = 'post_id'; }
Еще раз проверяем — теперь все в порядке:
Редактирование поста блога
Добавляем шаблон edit.blade.php
@extends('layouts.site') @section('content') <h1 class="mt-2 mb-3">Редактировать пост</h1> <form method="post" action="{{ route('post.update', ['id' => $post->post_id]) }}" enctype="multipart/form-data"> @csrf @method('PATCH') <div class="form-group"> <input type="text" class="form-control" name="title" placeholder="Заголовок" required value="{{ $post->title }}"> </div> <div class="form-group"> <textarea class="form-control" name="excerpt" placeholder="Анонс поста" required>{{ $post->excerpt }}</textarea> </div> <div class="form-group"> <textarea class="form-control" name="body" placeholder="Текст поста" rows="7" required>{{ $post->body }}</textarea> </div> <div class="form-group"> <input type="file" class="form-control-file" name="image"> </div> @isset($post->image) <div class="form-group form-check"> <input type="checkbox" class="form-check-input" name="remove" id="remove"> <label class="form-check-label" for="remove"> Удалить загруженное <a href="{{ $post->image }}" target="_blank">изображение</a> </label> </div> @endisset <div class="form-group"> <button type="submit" class="btn btn-primary">Сохранить</button> </div> </form> @endsection
PUT
, PATCH
или DELETE
, нам нужно добавить скрытое поле _method
для имитации этих HTTP-запросов — это можно сделать с помощью директивы @method('PATCH')
.
Добавляем новые маршруты
<?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'); Route::get('post/create', 'PostController@create')->name('post.create'); Route::post('post/store', 'PostController@store')->name('post.store'); Route::get('post/show/{id}', 'PostController@show')->name('post.show'); Route::get('post/edit/{id}', 'PostController@edit')->name('post.edit'); Route::patch('post/update/{id}', 'PostController@update')->name('post.update');
Реализуем метод edit()
<?php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; use Intervention\Image\Facades\Image; class PostController extends Controller { /* ... */ public function edit($id) { $post = Post::find($id); return view('posts.edit', compact('post')); } /* ... */ }
Реализуем метод update()
<?php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; use Intervention\Image\Facades\Image; class PostController extends Controller { /* ... */ public function store(Request $request) { $post = new Post(); $post->author_id = rand(1, 4); $post->title = $request->input('title'); $post->excerpt = $request->input('excerpt'); $post->body = $request->input('body'); $this->uploadImage($request, $post); $post->save(); return redirect() ->route('post.index') ->with('success', 'Новый пост успешно создан'); } /* ... */ public function update(Request $request, $id) { $post = Post::find($id); $post->title = $request->input('title'); $post->excerpt = $request->input('excerpt'); $post->body = $request->input('body'); $this->uploadImage($request, $post); $post->update(); return redirect() ->route('post.show', compact('id')) ->with('success', 'Пост успешно отредактирован'); } /* ... */ private function uploadImage(Request $request, Post $post) { // если надо удалить старое изображение if ($request->input('remove')) { if (!empty($post->image)) { $name = basename($post->image); if (Storage::exists('public/image/image/' . $name)) { Storage::delete('public/image/image/' . $name); } $post->image = null; } if (!empty($post->thumb)) { $name = basename($post->thumb); if (Storage::exists('public/image/thumb/' . $name)) { Storage::delete('public/image/thumb/' . $name); } $post->thumb = null; } // здесь сложнее, мы не знаем, какое у файла расширение if (!empty($name)) { $images = Storage::files('public/image/source'); $base = pathinfo($name, PATHINFO_FILENAME); foreach ($images as $img) { $temp = pathinfo($img, PATHINFO_FILENAME); if ($temp == $base) { Storage::delete($img); break; } } } } // если было загружено новое изображение $source = $request->file('image'); if ($source) { $ext = str_replace('jpeg', 'jpg', $source->extension()); // уникальное имя файла, под которым сохраним его в storage/image/source $name = md5(uniqid()); Storage::putFileAs('public/image/source', $source, $name. '.' . $ext); // создаем jpg изображение для с страницы поста размером 1200x400, качество 100% $image = Image::make($source) ->resizeCanvas(1200, 400, 'center', false, 'dddddd') ->encode('jpg', 100); // сохраняем это изображение под именем $name.jpg в директории public/image/image Storage::put('public/image/image/' . $name . '.jpg', $image); $image->destroy(); $post->image = Storage::url('public/image/image/' . $name . '.jpg'); // создаем jpg изображение для списка постов блога размером 600x200, качество 100% $thumb = Image::make($source) ->resizeCanvas(600, 200, 'center', false, 'dddddd') ->encode('jpg', 100); // сохраняем это изображение под именем $name.jpg в директории public/image/thumb Storage::put('public/image/thumb/' . $name . '.jpg', $thumb); $thumb->destroy(); $post->thumb = Storage::url('public/image/thumb/' . $name . '.jpg'); } } /* ... */ }
Заодно исправим метод store()
, потому что код загрузки изображения теперь в отдельном методе (чтобы не дублировать код). После обновления поста делаем редирект на страницу поста и показываем сообщение, что пост успешно отредактирован.
posts
два почти одинаковых поля image
и thumb
, так и работать с ними неудобно. Надо сохранять в БД только уникальное имя файла изображения, тогда при обновлении поста будет удобнее удалять старые изображения.
- Мини-блог на Laravel, часть 4. Создание нового поста, загрузка и обрезка изображения
- Мини-блог на Laravel, часть 3. Постраничная навигация, layout-шаблон и поиск по блогу
- Блог на Laravel 7, часть 12. Доп.страницы сайта в панели управления и в публичной части
- Блог на Laravel 7, часть 11. Панель управления — назначение ролей и прав для пользователей
- Блог на Laravel 7, часть 10. Личный кабинет — CRUD-операции над постами и комментариями
- Блог на Laravel 7, часть 9. Панель управления — создание, публикация, удаление комментариев
- Блог на Laravel 7, часть 8. Панель управления — CRUD для категорий, тегов и пользователей
Поиск: Laravel • MySQL • PHP • Web-разработка • База данных • Блог • Фреймворк • Шаблон сайта • Практика • route • Маршрут