#1.概要
railsでアプリを作っていて、投稿が多くなってきた時にタグ機能で整理したい!タグ検索機能を実装して、見やすいサイトを作りたい!こんなこと思う人多いのではないでしょうか。
今回は簡単なrails投稿アプリを開発しながら、タグ検索機能を実装して行きます。
※なお、今回はGemを使っていません&&用意されたタグをユーザーが選択してタグ付けとする予定なのでユーザーがタグを作成する機能はここでは想定していません。
ruby 2.5.6
Rails 6.0.0
#2.タグ付け機能の実装
##2.1.事前準備
まずタグ機能を実装するために投稿するだけのサンプルアプリを作っていきます。
cd
cd desktop
rails new TagSample
それでは投稿周りの機能をscaffoldを利用して一瞬で作成します。
cd TagSample
rails generate scaffold post body:text title:string
rails db:migrate
##2.2.モデル作成
まず、tagモデルを作成します。
rails generate model tag name:string
続いてpostモデルとtagモデルの中間テーブルとなるpost_tagモデルを作成して行きます。中間テーブルの作成にはreferencesというパラメーターを用いてpostモデル及びtagモデルの中間テーブルであることを示します。
rails generate model post_tag post:references tag:references
rails db:migrate
これにてモデルの作成は完了しました。db/schema.rbが以下のようになっているはずです。
ActiveRecord::Schema.define(version: 2019_10_30_135534) do
create_table "post_tags", force: :cascade do |t|
t.integer "post_id", null: false
t.integer "tag_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["post_id"], name: "index_post_tags_on_post_id"
t.index ["tag_id"], name: "index_post_tags_on_tag_id"
end
create_table "posts", force: :cascade do |t|
t.text "body"
t.text "title"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "tags", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "post_tags", "posts"
add_foreign_key "post_tags", "tags"
end
##2.3.アソシエーションの設定
まず、中間テーブルであるpost_tagモデルにはreferencesにより以下のような記述がすでにあるはずです。
これはpost_tagをpostモデル、tagモデルそれぞれ一つづつと関連させています。
class PostTag < ApplicationRecord
belongs_to :post
belongs_to :tag
end
続いてtagモデルとpostモデルにそれぞれを関連づける設定を追記します。
class Tag < ApplicationRecord
has_many :post_tags, dependent: :destroy
has_many :posts, through: :post_tags
end
ここでは、
has_many :post_tags, dependent: :destroy
によりtagモデルとpost_tagモデルの関連づけ。及びタグが削除された時にpostモデルとの関連付けも自動で削除するように設定しています。
has_many :posts, through: :post_tags
これによって、中間テーブルであるpost_tagモデルを介してのpostモデルとの関連付けを記述しています。
また、以下に書きますが、postモデルに対してもその逆の記述をし、中間テーブルを介してtagモデルと関連付けています
class Post < ApplicationRecord
has_many :post_tags, dependent: :destroy
has_many :tags, through: :post_tags
end
これでアソシエーションの記述は終了です。
##2.4.seed.rbにタグレコードを追加
seedを以下のように記述することで、tagテーブルのnameカラムに値を追加してタグを生成します。ここでは適当に有名なコーヒーチェーンの名前を5個くらい書いておきました。
##省略
#ここから追記
Tag.create([
{ name: 'スターバックス' },
{ name: 'ドトール' },
{ name: 'エクセルシオール'},
{ name: 'タリーズ'},
{ name: 'サンマルク'},
{ name: 'コメダ'}
])
#ここまで
以下のコマンドでdbに変更を反映させます
rails db:seed
dbにdb/seedで入れた値は入ったでしょうか?rails consoleでTag.allと叩くことでdbに適切に値が入っているか確認できます。
##2.5.viewのformでtagを選択できるようにする
続いてtagを投稿ページで選択できるように、_form.html.erbにチェックボックスを追記して行きます。
#省略
#追記ここから
<div class="check_box">
<span>タグ</span>
<%= form.collection_check_boxes(:tag_ids, Tag.all, :id, :name) do |tag| %>
<div>
<%= tag.label do %>
<%= tag.check_box %>
<%= tag.text %>
<% end %>
</div>
<% end %>
</div>
#追記ここまで
<div class="actions">
<%= form.submit %>
</div>
<% end %>
collection_check_boxesについては詳細の説明は省きます。ググってください(いずれ追記します。。。)
##2.5.コントローラーでストロングパラメーターを追記
posts/controllerにformで追加したプロパティ名であるtag_idsを以下のように追記します。
tag_idsは複数のタグが渡されるので、配列の形式でpost_paramsに渡すよう記述をします。
#省略
def post_params
params.require(:post).permit(:body, :title, tag_ids: [])
end
end
##2.6.投稿にtagが表示されるようviewを追記
渡された複数のタグをeach文で表示させるためそれぞれshowとindexに以下を追記。
<% @post.tags.each do |tag| %>
<%= tag.name %>
<% end %>
<% post.tags.each do |tag| %>
<%= tag.name %>
<% end %>
#3.タグ検索機能の実装
最後にここからタグ検索機能を実装して行きます
選択したtagの投稿のみ取得するようにindex.html.erbを以下のように編集して行きます。
<p id="notice"><%= notice %></p>
<h1>Posts</h1>
#ここから追記
<%= form_tag posts_path, method: :get, class: 'boards__searchForm' do %>
<%= select_tag :tag_id,
options_from_collection_for_select(Tag.all, :id, :name, params[:tag_id]),
{
prompt: 'タグで絞り込み検索',
onchange: 'submit(this.form);'
}
%>
<% end %>
#ここまで
#省略
続いてposts_controllerのindexアクションを以下のように編集します。
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
#ここから追記
@posts = params[:tag_id].present? ? Tag.find(params[:tag_id]).posts : Post.all
#ここまで
end
以下の記述により、tag_idがセットされていたらTagから関連づけられたpostsを呼び、tag_idの指定がなければ、全ての投稿を表示するよう記述されています。
Tag.find(params[:tag_id]).posts : Post.all
これで完成です。ここまでやると以下のような簡単なタグ生成アプリができるのではないでしょうか?
#4.終わり
少々、説明が雑ですが(ごめんなさいリライトします)これでタグ検索機能の実装の完了とします。
#5.終わりに
GeekSalonという大学生限定のプログラミングコミュニティではメンターが精力的に記事をアウトプットしています。
こちらはwebメンターのまこっちゃん
https://qiita.com/makoto15