Тэги. 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