タグ機能を実装する(Toxi法)
多対多の関連付けをする
下の表のような3つのモデルを作ります。他のカラムを追加しても良いですが、最低限必要なカラムだけ表示しています。
次にモデル間の関連付けをします。
関連付けにはhas_many、belongs_toを使います。
・post.rb
class Post < ApplicationRecord has_many :tagmaps, dependent: :destroy has_many :tags, through: :tagmaps end
・tag.rb
class Tag < ApplicationRecord has_many :tagmaps, dependent: :destroy has_many :posts, through: :tagmaps end
・tagmap.rb
class Tagmap < ApplicationRecord belongs_to :post belongs_to :tag end
タグ情報を新規で登録できるようにする
次に、タグ情報を登録できるようにしていきます。
posts.controller.rbのcreateに追加していきます。
def create @post = current_user.posts.build(post_params) tag_list = params[:post][:tag_name].delete(' ').delete(' ').split(',') if @post.save @post.save_posts(tag_list) redirect_to posts_path, success: '投稿しました' else flash.now[:danger] = '投稿に失敗しました' render :new end end
今回追加したのは、3行目と5行目のコードです。
送られてきたタグ情報をsplit(“,”)でカンマ区切りにして、deleteでタグの前後にある半角スペースと全角スペースを消しています。
そして、save_postsメソッドでタグを保存していきます。save_postsメソッドはモデル側で設定します。
post.rb
def save_posts(tags) current_tags = self.tags.pluck(:tag_name) unless self.tags.nil? old_tags = current_tags - tags new_tags = tags - current_tags old_tags.each do |old_name| self.tags.delete Tag.find_by(tag_name: old_name) end new_tags.each do |new_name| post_tag = Tag.find_or_create_by(tag_name: new_name) self.tags << post_tag end end
こんな感じで、既存のタグが被らないように、データベースに保存していきますが、ちょっとうまく説明できないので割愛させてもらいます。すみません。
次に、posts.controller.rbのupdateとeditも変更して、編集もできるようにしておきます。また、フォームも変更しておきます。
posts.controller.rb
def edit @post = current_user.posts.find(params[:id]) @tag_list = @post.tags.pluck(:tag_name).join(',') end def update @post = current_user.posts.find(params[:id]) tag_list = params[:post][:tag_name].delete(' ').delete(' ').split(',') if @post.update(post_params) @post.save_posts(tag_list) redirect_to posts_path, success: '投稿を更新しました' else flash.now[:danger] = '投稿の更新に失敗しました' render :edit end end
form.html.erb
<div class="tag"> <%= form.label :tag %> <%= form.text_field :tag_name, value: @tag_list %> </div>
タグの表示
posts.controller.rb
def index if params[:tag_id] @tag_list = Tag.all #追加 @tag = Tag.find(params[:tag_id]) @posts = @tag.blogs.published.order(time: "DESC").page(params[:page]).per(10) @posts_side = Post.published.order(time: "DESC") else @tag_list = Tag.all #追加 @posts = Post.published.order(time: "DESC").page(params[:page]).per(10) @posts_side = Post.published.order(time: "DESC") end respond_to do |format| format.html format.rss { render :layout => false } end end
view
<% @tag_list.each do |list| %> <p><%=link_to list.tag_name,posts_path(tag_id:list.id)%><%= list.posts.published.count %></p> <% end %>