Тэги. Laravel - блог на Voyager
воскресенье, 13 января 2019 06: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
- Tools -> BREAD -> posts внизу розовая кнопка с сердечком - Создать отношения (вот юмористы!). Жмем на неё, выбираем Многие ко многим
- Дальше выбираем Tag, и в 3-ем поле пишем App\Tag
- Устанавливаем сводную таблицу - Post_tag из списка
- Отобразить из tags - title
- Хранить tags - id
- Разрешить тегирование - да
Приводим 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