🏷️

[投稿に複数Tag] 準備・投稿・詳細画面で出力編

2024/07/05に公開

今回の私のPF内で重要項目になっている1つが、そう、[🏷️tag]機能!!
また違う機会で実装する時助けになるように書いておこうと思います。

実装したい事

1.1つの投稿に対して複数のタグをつけたい
2.詳細画面で複数表示されるようにしたい
3.編集画面でタグを一気に付け替えれるようにしたい
4.タグで検索できるようにしたい
(5.タグ検索を複数でできるようにしたい)

5は余裕があれば実装する予定なので今はかけるかわかりません🤥
今回は0、1,2編!

0.事前準備

まず複数タグをつけるということで大事になってくるのが関連付け(アソシエーション)

・投稿は複数タグをつけれる
・タグは複数の投稿につく

つまり[多対対]になるので中間テーブルが必要になる👍🏻
ちなみに私はパソコンのバグでモデル名をCafe→Postに変えたけど元々はCafeだったから中間テーブルがcafe_tagsになってるけど、中間テーブル名もpost_tagsとかに直して名前でどこと繋がってるのかわかるようにした方がいい!(なんでしなかったかってめんどくさかった😶怠惰)

post model
class Post < ApplicationRecord
  has_many :cafe_tags
  has_many :tags, through: :cafe_tags
end
tag model
class Tag < ApplicationRecord
  has_many :cafe_tags
  has_many :posts, through: :cafe_tags
end
cafe_tag model
class CafeTag < ApplicationRecord
    belongs_to :post
    belongs_to :tag
end

throughって引用とかって意味。
今回は多分名前がcafe_tagのままいってしまったからか、最初[has_many :posts]とかで打ってたらどこですか?みたいなエラーが出て、

through(引用は): :cafe_tags(だよー)

って指定したら解決しました💟やしちゃんとモデル名をpost_tagにしてたら撃たなくて良かったかも🤪
結果めんどくさくなってるという。。。だって途中まで作ってたんやもん😒

あとは先にタグ準備しとく。

1.投稿に対して複数のタグをつける

レイアウトはガン無視で最低限の記述だけにします!!

new.html.erb
<%= form.label :tag_ids, "タグ" %>
<% Tag.all.each_slice(3) do |tag_group| %>
<% tag_group.each do |tag| %>
<%= form.check_box :tag_ids, { multiple: true }, tag.id, nil %>
<%= tag.name %>
<% end %>
<% end %>

・postテーブル内にはtag_ids(複数)ってカラムはないけどアソシエーション済みなので指定できる。

<% Tag.all.each_slice(3) do |tag_group| %> ???

each_slice(3) とすることで、Tag.all から取得したタグを 3 つずつのグループに分けて処理。
今回は大量にあるタグを3列に分けたかったので記載。

その後<% tag_group.each do |tag| %>で3つに分けたグループからeachで出力。
出力方法は今回チャックボックスにしました😃!!

{ multiple: true } 何これ???

チェックボックスが複数選択可能であることを示すオプション。
このオプションを指定すると、複数の値を同時に送信できる!!!

tag.idを書く理由

チェックボックスの値になんのID持ってくんの?を指定している。
これにより、各タグごとに異なる値(タグid.1とか2とか)がチェックボックスの値として設定される

nil を書く理由

チェックボックスの checked 状態が指定されていないことを示すための引数。
@post が既に選択されたタグを持っている場合、そのタグのチェックボックスがチェックされた状態になります。
編集の時とかは元々選んでたものにチェックついてるようになる。すげー

出力結果

おおおーーーーー。笑

見た目が揃ったところで、次はコントローラーへ行こう👍🏻!!!

def new
 @post = Post.new
end 
    
def create
 @post = Post.new(post_params.except(:tag_ids))
 if @post.save
  @post.tag_ids = params[:post][:tag_ids] if params[:post][:tag_ids].present?
  redirect_to admin_post_path(@post.id)
 else
  render :new
 end
end

def post_params
  params.require(:post).permit(:name, :address, :hours, :days_open, :review, post_images: [], tag_ids: [])
end

うんうん。今回よくわからんかったんは、そう、

(post_params.except(:tag_ids))
@post.tag_ids = params[:post][:tag_ids] if params[:post][:tag_ids].present?

paramsの中の tag_ids: []

どう考えてもこいつら。。少しでも応用っぽくなると止まる思考と手🙋‍♀️厄介〜

post_params.except(:tag_ids)を書く理由

まずexceptって除外っていう意味🙂タグもフォームから送信するのになぜ除外するの?
簡単にいうとセキュリティ強化のため。
本来tags_idってpostテーブルにないもの。それを何も指定せず普通に送るとセキュリティ的によくないらしい。
Post.new(post_params.except(:tag_ids))
とすることでpost_paramsから:tag_idsを除いた残りのパラメーターを使用して新しいPostオブジェクトを作成できる。
これにより、不必要なデータやセキュリティリスクを減らしつつ、必要な情報のみをPostオブジェクトに設定することができる。ふーん🙂

なるほどね。。でもそこで出てくる疑問が、そう、、「じゃあどうやってtag_idsを送信すんの?🤨」
それを叶えてるのが

@post.tag_ids = params[:post][:tag_ids] if params[:post][:tag_ids].present?

です!!!

このコードは、通常はStrong Parametersで設定されたもの(post_params)に含まれないtag_idsというパラメーターを、@postオブジェクトにセットするために使われます。
例えば今回みたいな関連付けられたモデルのIDの受け渡しに使用する🤓ほぉ〜。。
下のparamsの中にtag_ids: []がある理由はここで使うからね!なーるー

2.詳細画面で複数表示されるようにしたい

いざ!出力たーいむ!!
ちなみにコントローラーは普通です!ありがたい!

controller
def show
 @post = Post.find(params[:id])
end

今回もレイアウトは抜きにして最低限コード

show.html.erb
<h6 class="title">タグ</h6>
<% @post.tags.each do |tag| %>
<%= tag.name %>
<% end %>

うん。普通ちゃう???そんな珍しい事ない気がするわ。ふーん。普通でええんや。tagsでsついてる位かな?
私はレイアウトのカードが好きやしタグっぽくしたかったので

<div class="card" style="width: 500px;">
  <div class="card-body">
   <h6 class="card-title"><i class="fa-solid fa-tags fa-sm" style="color: #515053;"></i> タグ</h6>
    <% @post.tags.each do |tag| %>
     <span class="badge bg-info">#<%= tag.name %></span>
    <% end %>
  </div>
</div>

にしました!!こだわりとしては〜〜
'style="width: 500px;' と指定してタグの数によってカードの幅が変わらないようにした事。
'<%= tag.name %>'の前に'#'を書いて'#タグ名'と出力されるようにした事。
'badge(バッジ)'をつけて目立ちやすくした事。 です!!!🫡

うん!!可愛いんちゃう〜〜〜????👻

感想

投稿時とかはエラーもだいぶ出たけど思ってたよりはスムーズにできたかな〜って感じ!!
何よりも見た目が可愛くできて大満足!!!!!!
編集とかも頑張らないとな〜〜〜!!

Discussion