Создаем блог на Laravel
суббота, 12 января 2019 03:42:18, написал admin
В этой статье я расскажу, как быстро развернуть простой блог на Laravel. Мы воспользуемся админкой Voyager c её генератором BREAD, настроим показ категорий и постов на фронтенде, добавим карту сайта и форму обратной связи, рассмотрим тонкости настройки самого Laravel.
Ставим последнюю версию (на момент написания статьи это 5.7) Laravel - https://laravel.com/docs/5.7/installation Я ставил через composer в текущую папку
composer create-project --prefer-dist laravel/laravel ./
После установки генерируем ключ
php artisan key:generate
Генерируем вью для входа и регистрации
php artisan make:auth
Теперь зайдите на сайт и проверьте, работает ли он.
Дальше ставим админку Voyager: https://docs.laravelvoyager.com/getting-started/installation
composer require tcg/voyager
настраиваем доступ к базе данных в файле .env
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=database DB_USERNAME=user DB_PASSWORD=password
и выполняем
php artisan voyager:install --with-dummy
- поставит с демо-данными. Если хотите без них, то вызывайте без ключа --with-dummy
Команда у меня не выполнилась из-за ошибки. Подробнее тут: Laravel и ошибка MySql Specified key was too long error
Исправляем ошибку по статье и идем дальше: заходим в адимнку site/admin и смотрим, что панель встала.
Далее включаем русский. Сначала для всего сайта. В конфиге (config/app.php) находим строку
'locale' => 'en'
И меняем на
'locale' => 'ru'
Качаем https://github.com/caouecs/Laravel-lang/tree/master/src/ru все файлы с переводом, бросаем их в папку resources/lang/ru
В конфиге config/voyager.php приводим к такому виду
/*
* Set whether or not the multilingual is supported by the BREAD input.
*/
'enabled' => true,
/*
* Set whether or not the admin layout default is RTL.
*/
'rtl' => false,
/*
* Select default language
*/
'default' => 'ru',
/*
* Select languages that are supported.
*/
'locales' => [
'en',
'ru',
],
Проверяем, идем в site/admin (важно! Надо непременно обновить Dashboard) и проверяем, что там контент на русском.
После установки в админке нам доступны статьи и посты с категориями. Давайте сделаем вывод страниц из таблицы pages на самом сайте.
Первым делом настраиваем роутер. Файл app\routers\web.php, дописываем
Route::get('/{page}.html', 'PageController@index')->name('page');
Здесь первый параметр – правило роутера (слаг страницы и расширение .html), далее контроллер и экшен. И наконец ->name('page') – название роутера (используется для вызовов в коде).
Далее создаем контроллер PageController через команду
php artisan make:controller PageController
В app\Http\Controllers у нас появился PageController. Приводим его к такому виду:
<?php
namespace App\Http\Controllers;
use Illuminate\Routing\Controller as BaseController;
use TCG\Voyager\Models\Page;
class PageController extends BaseController
{
/**
* Show page.
*
* @param string $slug
* @return Response
*/
public function index($slug)
{
$page = Page::where('slug', $slug)->firstOrFail();
return view('page', ['page' => $page]);
}
}
В app\resources\views\page.blade.php пишем такой код:
@extends('layouts.app')
@section('title')
<?= $page->title ?>
@endsection
@section('content')
<h1>{{ $page->title }}</h1>
<img style="width: 100%;" src="{{ Voyager::image( $page->image ) }}" />
<p>{!! $page->body !!}</p>
@endsection
Идем в админку, создаем страницу со слагом about и любым заголовком и текстом. Открываем её по адресу http://ваш-сайт/about.html
Теперь приступим к блогу. Создадим BlogController и роутеры к нему. Начнем с роутеров, добавьте в app\routers\web.php следующие строки :
Route::get('/blog', 'BlogController@index')->name('blog.index');
Route::get('/blog/category/{slug}', 'BlogController@category')->name('blog.category');
Route::get('/blog/tag/{slug}', 'BlogController@tag')->name('blog.tag');
Route::get('/blog/archive/{year}/{month}', 'BlogController@archive')->name('blog.archive');
Route::get('/blog/{slug}.html', 'BlogController@show')->name('blog.show');
Вы видите здесь экшен tag. По умолчанию в Voyager нет такой модели и мы позже создадим её, используя интерфейс Voyager
В контроллер блога вписываем:
<?php
namespace App\Http\Controllers;
use Illuminate\Routing\Controller as BaseController;
use App\Category;
use App\Post;
use App\Tag;
class BlogController extends BaseController
{
// количество записей на странице
public $item_count = 20;
/**
* Show the posts
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
$models = Post::orderBy('created_at', 'DESC')->paginate($this->item_count);
return view('blog.index', ['models' => $models]);
}
/**
* Show the posts of category.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function category($slug)
{
$category = Category::where('slug', '=', $slug)->firstOrFail();
$models = Post::where('category_id', '=', $category->id)
->orderBy('created_at', 'DESC')
->paginate($this->item_count);
return view('blog.category', [
'category' => $category,
'models' => $models,
]);
}
/**
* Show the posts of tag.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function tag($slug)
{
$tag = Tag::where('slug', '=', $slug)->firstOrFail();
$tag_id = $tag->id;
$models = Post::whereHas('tags', function($q) use($tag_id) {
$q->where('id', $tag_id);
})->orderBy('created_at', 'DESC')->paginate($this->item_count);
return view('blog.tag', [
'models' => $models,
'tag' => $tag
]);
}
/**
* Show the posts of archive.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function archive($year, $month)
{
$models = Post::whereYear('created_at', '=', $year)
->whereMonth('created_at', '=', $month)
->orderBy('created_at', 'DESC')
->paginate($this->item_count);
return view('blog.archive', [
'models' => $models,
'year' => $year,
'month' => $month,
]);
}
/**
* Show the post of slug.
*
* @param string $slug
* @return Response
*/
public function show($slug)
{
$article = Post::where('slug', '=', $slug)->firstOrFail();
return view('blog.show', ['article' => $article]);
}
}
Обратите внимание на то, как здесь получается пагинация. В Yii например для этих целей используется датапровайдер, который затем подставляется в виджеты. Здесь же мы вызываем у экземпляра класса метод paginate(), где входной параметр – число записей на страницу, и получаем список моделей плюс пагинацию. Пагинация выводиться во вью через
{{ $models->links() }}
Более подробно здесь https://laravel.com/docs/5.7/pagination
Я не буду приводить листинг вью для категории, архива, и тэга. Приведу только для index, у остальных все аналогично, меняется только заголовок и хлебные крошки (о них речь потом).
Итак, создаем файлы в папке app\resources\views\blog\index.blade.php и app\resources\views\blog\_view.blade.php
Код index.blade.php:
@extends('layouts.app')
@section('title', 'Блог')
@section('content')
@foreach ($models as $model)
@include('blog._view', ['model' => $model])
@endforeach
{{ $models->links() }}
@endsection
Разметка blade очень похожа на twig из Симфони. Конструкция @extends указывает, от какого шаблона наследоваться.
@section – это блок, который потом выводиться в лайоуте командой @yield
@foreach – обход массива. Внутри циклам мы рендерим _view.blade.php через команду @include. Файл имеет следующее содержимое:
<h2><a href="/blog/{{ $model->slug }}.html">{{ $model->title }}</a></h2>
<?php $data = new Jenssegers\Date\Date($model->created_at);?>
<p><i class="fa fa-calendar"></i> {{ $data->format('l, j F Y H:i:s') }}, написал <i class="fa fa-user"></i> <a href="#">{{ $model->authorId()->first()->name }}</a></p>
<p>{!! nl2br($model->excerpt) !!} </p>
<p><a href="{{ route('blog.show', ['slug' => $model->slug]) }}" class="btn btn-info pull-right">Читать дальше</a></p>
@if ($model->tags)
@foreach ($model->tags as $tag)
<a href="{{ route('blog.tag', ['slug'=> $tag->slug]) }}"><span class="badge badge-info">{{ $tag->title }}</span></a>
@endforeach
@endif
@if (!empty($model->category))
<a href="{{ route('blog.category', ['slug' => $model->category->slug]) }}">
<span class="badge badge-lg badge-success">{{ $model->category->name }}</span>
</a>
@endif
<hr/>
Тут стоит отметить работу с датами. Мне не получилось вывести название месяца на русском ни встроенными средствами Laravel (https://carbon.nesbot.com/docs/), ни чистым php (через setlocale)
В app/providers/AppServiceProvider.php я прописал
public function boot()
{
Schema::defaultStringLength(191);
setlocale(LC_ALL, 'ru_RU.UTF-8');
Carbon::setLocale(config('app.locale'));
}
Но ничего не помогло. Месяц по-прежнему выводился по-английски. Наткнулся на совет – поставить пакет jenssegers/date
Помогло. Сам пакет ставим отсюда: https://github.com/jenssegers/date
composer require jenssegers/date
Потом в config/app.php добавляем
'Date' => Jenssegers\Date\Date::class,
И вызываем в _view.blade.php
<?php $data = new Jenssegers\Date\Date($article->created_at);?>
{{ $data->format('l, j F Y H:i:s') }}
Выведет
суббота, 12 января 2019 07:42:18
Настраиваем вью для показа поста show.blade.php
@extends('layouts.app')
@section('breadcrumbs', Breadcrumbs::render('post', $article))
@section('title')
{{ $article->title }}
@endsection
@section('content')
<h2>{{ $article->title }}</h2>
<?php $data = new Jenssegers\Date\Date($article->created_at);?>
<p><i class="fa fa-calendar"></i> {{ $data->format('l, j F Y H:i:s') }}, написал <i class="fa fa-user"></i> <a href="#">{{ $article->authorId()->first()->name }}</a></p>
<p>{!! nl2br($article->excerpt) !!} </p>
<p>{!! $article->body !!}</p>
@if ($article->tags)
@foreach ($article->tags as $tag)
<a href="{{ route('blog.tag', ['slug'=> $tag->slug]) }}"><span class="badge badge-info">{{ $tag->title }}</span></a>
@endforeach
@endif
@if ($article->category)
<a href="{{ route('blog.category', ['slug'=> $article->category->slug]) }}"><span class="badge badge-success">{{ $article->category->name }}</span></a>
@endif
<div>
<h3>Комментарии к статье</h3>
@widget('DisqusWidget', ['page_id' => route('blog.show', ['slug'=> $article->slug], false)]);
</div>
@endsection
Тут есть несколько важных моментов.
Первое – вызов хлебных крошек
@section('breadcrumbs', Breadcrumbs::render('post', $article))
Подробнее о них написал здесь: Laravel - вывод хлебных крошек
Второе – печать данных без экранирования тегов (аналог raw в twig)
{!! $article->body !!}
Третье – про подключение тэгов написал здесь: Тэги. Laravel - блог на Voyager
@if ($article->tags)
@foreach ($article->tags as $tag)
<a href="{{ route('blog.tag', ['slug'=> $tag->slug]) }}"><span class="badge badge-info">{{ $tag->title }}</span></a>
@endforeach
@endif
И наконец, в четвертых – вызов виджета комментариев Disqus. Про него написал в этой статье Laravel - создание виджета Disqus.
<div>
<h3>Комментарии к статье</h3>
@widget('DisqusWidget', ['page_id' => route('blog.show', ['slug'=> $article->slug], false)]);
</div>
Осталось по мелочам.
Давайте создадим карту сайта по этой статье: Laravel - создание карты сайта
А форму обратной связи - по этой: Laravel - форма обратной связи
Laravel Voyager Laravel