Создаем блог на 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
  

Поделиться статьей с друзьями:

  

Комментарии к статье