Тэги. Laravel - блог на Voyager

воскресенье, 13 января 2019 05:42:06, написал Admin

По умолчанию Voyager не имеет поддержки тегов. Как в общем-то справедливо кто-то заметил, Voyager - это админка, а не полноценная CMS. Давайте попробуем создать новую таблицу тэгов средствами Voyager и подключим её к постам.

Заходим в /admin

Идем в Tools -> Database

Жмем Создать новую таблицу

Заполняем "Название таблицы" - tags

Устанавливаем "Создать модель для этой таблицы?" в "Да, пожалуйста"

Добавляем поля id, slug, title, weight. Жмем на кнопку внизу "Добавить метки времени". Полям slug, title ставим уникальные ключи

Создаем таблицу.

Теперь идем в Tools -> BREAD и жмем Добавить BREAD у таблицы tags.

Аналогично создаем таблицу post_tag, но BREAD не добавляем. Поля post_id, tag_id

Теперь основная магия. Создаем отношения для таблицы posts

  1. Tools -> BREAD -> posts внизу розовая кнопка с сердечком - Создать отношения (вот юмористы!). Жмем на неё, выбираем Многие ко многим
  2. Дальше выбираем Tag, и в 3-ем поле пишем App\Tag
  3. Устанавливаем сводную таблицу - Post_tag из списка
  4. Отобразить из tags - title
  5. Хранить tags - id
  6. Разрешить тегирование - да

Приводим App\Tag к такому виду:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
    public function save(array $options = [])
    {
        if (empty($this->slug)) {
            $this->slug = str_slug($this->title);
            $this->weight = 1;
        }

        parent::save();

    }
}

Что здесь происходит. Когда мы создаем тег из админки таблицы tags, то поле slug обязательно для заполнения. Но когда тэг создается на лету, из поста - то там заполняется только title. А слаг пустой. Поэтому мы проверяем в функции save(), пустой ли слаг, и если да - то заполняем его через Laravel - функцию str_slug. И ставим weight (вес, количество использований тега в постах) в 1.

Дальше копируем модель TCG\Voyager\Models\Post в App\Post и редактируем BREAD для таблицы post. Заменяем в Название таблицы TCG\Voyager\Models\Post на App\Post

Приводим App\Post к вот такому виду

<?php

namespace App;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Request;
use TCG\Voyager\Facades\Voyager;
use TCG\Voyager\Traits\Resizable;
use TCG\Voyager\Traits\Translatable;

class Post extends Model
{
    use Translatable,
        Resizable;

    protected $translatable = ['title', 'seo_title', 'excerpt', 'body', 'slug', 'meta_description', 'meta_keywords'];

    const PUBLISHED = 'PUBLISHED';

    protected $guarded = [];

    protected $ids = [];

    public function save(array $options = [])
    {
        // If no author has been assigned, assign the current user's id as the author of the post
        if (!$this->author_id && Auth::user()) {
            $this->author_id = Auth::user()->id;
        }

        $query = "SELECT tag_id FROM post_tag WHERE post_id = ? GROUP BY tag_id ";
        $models = DB::select($query, [$this->id]);
        $ids_old = [];
        foreach ($models as $model) {
            $ids_old[] = $model->tag_id;
        }

        parent::save();

        $ids_new = Request::input('post_belongstomany_tag_relationship',[]);

        $ids = array_unique(array_merge($ids_old, $ids_new));
        $s = implode(',',$ids);

        $query = "SELECT tag_id, count(*) as count_tags FROM post_tag WHERE tag_id in ($s) GROUP BY tag_id ";
        $models = DB::select($query);
        foreach ($models as $model) {
            $weight = $model->count_tags;
            $is_update = false;
            if (in_array($model->tag_id,$ids_new) && !in_array($model->tag_id,$ids_old)) {
                $weight++;
                $is_update = true;
            } elseif (!in_array($model->tag_id,$ids_new) && in_array($model->tag_id,$ids_old)) {
                $weight--;
                $is_update = true;
            }
            $query = "UPDATE tags SET weight = ? WHERE id = ? ";
            DB::update($query, [$weight, $model->tag_id]);
        }
    }

    public function authorId()
    {
        return $this->belongsTo(Voyager::modelClass('User'), 'author_id', 'id');
    }

    /**
     * Scope a query to only published scopes.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePublished(Builder $query)
    {
        return $query->where('status', '=', static::PUBLISHED);
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function category()
    {
        return $this->belongsTo('App\Category', 'category_id');
    }

    public function tags()
    {
        return $this->belongsToMany('App\Tag', 'post_tag');
    }
}

В чем здесь суть. При сохранении поста мы получаем список старых тегов. Как получить список новых тегов средствами самого Laravel я к сожалению так и не разобрался. Но заметил, что в $_POST приходит переменная post_belongstomany_tag_relationship со списком новых тэгов. Поэтому получаем массив айдишников новых тегов из post_belongstomany_tag_relationship , и делаем по всем (и старым, и новым тегам) запрос на количество использований. Потом проходимся в цикле, и тем тегам, которые были удалены - уменьшаем вес на 1, тем которые добавлены - прибавляем единицу. Возможно есть какое-то другое более изящное решение, сильно не изучал этот вопрос.

Вызов тегов поста во вью:

    @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

 

Laravel Voyager Tags Laravel
  

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

  

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