Тэги. 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 к такому виду:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?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 к вот такому виду
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | <?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, тем которые добавлены - прибавляем единицу. Возможно есть какое-то другое более изящное решение, сильно не изучал этот вопрос.
Вызов тегов поста во вью:
1 2 3 4 5 | @ 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