行いたいこと
- 本の投稿時にカテゴリタグを追加する
- カテゴリタグで検索が出来る
環境
- Ruby3.1.2
- Rails6.1.7
- device、bootstrap導入済み
- Bookモデル作成済み
1.カラムの追加
Bookモデルにタグを保存するために新しくカラムtag
を作成
# rails g migration add追加したいカラム名To追加したいモデル名 追加したいカラム名:データ型
rails g migration addTagToBook tag:string
class AddTagToBook < ActiveRecord::Migration[6.1]
def change
add_column :books, :tag, :string
end
end
$ rails db:migrate
2.コントローラー作成、バリデーション設定
バリデーション、ストロングパラメータの設定
validates :tag,presence:true
books_controllerのストロングパラメータに:tag
を追加
private
def book_params
params.require(:book).permit(:title,:body,:rating,:tag)#tagを追加
end
TagSearchesコントローラーを作成
$ rails g controller TagSearches
コントローラーへの記述は以下の通り
今回は部分一致で検索を実施
@modelはresultのviewを他から流用しており、そこで@modelを使用しているため指定
searchとしての処理には不必要であり、後述のviewで使用しないなら削除してOK
class TagSearchesController < ApplicationController
def search
@model = Book #search機能とは関係なし
@word = params[:word]
@books = Book.where("tag LIKE?","%#{@word}%")
render "searches/result"
end
end
3.viewページの作成
投稿フォームへの追加
tagを追加するためのtext_fieldを作成
<div class="form-group">
<%= f.label :Tag %>
<%= f.text_field :tag, class: 'form-control book_tag' %>
</div>
タグ検索用の検索ボックスの作成
<% if user_signed_in? %>
<div class="search_form">
<%= form_with url: tag_search_path, local: true,method: :get do |f| %>
< %= f.text_field :word %>
<%= f.submit "タグ検索" %>
<% end %>
</div>
<% end %>
検索結果の表示
今回他で作成した検索結果表示ページを流用
book.tag
で保存されたタグを呼び出す
<h2><%= @model %>s search for "<%= @word %>"</h2>
# Books search for "<%= @word %>"と記載すれば@model不要
<table class="table table-hover table-inverse">
〜省略〜
<thead>
<tr>
<th></th>
<th>Title</th>
<th>Opinion</th>
<th>Tag</th>
</tr>
</thead>
<% @books.each do |book| %>
<tbody>
<tr>
<td><%= image_tag book.user.get_profile_image, size:'50x50' %></td>
<td><%= book.title %></td>
<td><%= book.body %></td>
<td><%= book.tag %></td> #追加!
<td><%= render "favorites/nice", book: book %></td>
<td>コメント数:<%= book.book_comments.count %></td>
<td class ="px-0"><%= render "books/rating", book: book %></td>
</tr>
</tbody>
<% end %>
<% end %>
</table>
またindexにも同様に <%= book.tag %> で表示
<% books.each do |book| %>
<tr>
<td><%= link_to(book.user) do %>
<%= image_tag book.user.get_profile_image, size:'50x50' %>
<% end %>
</td>
<td class ="px-0"><%= link_to book.title,book %></td>
<td><%= book.body %></td>
<td><%= book.tag %></td> #追加
<td id="js-book_favorite_<%= book.id %>">
応用
タグにリンクを付与し、クリックすると、そのタグ名で検索を行う
上記記事にも投稿しましたが、link_to〜_path( )
の( )に値を設定することで、任意の値を一緒に送信することが出来る
これを活用し
<%= link_to book.tag, tag_search_path(word: book.tag) %>
とリンクを作成することで、送信時一緒に:word
としてbook.tag
が送られる
投稿済みの本に設定してあるタグから検索する方法
f.text_fieldを削除し、下記のコードを追加
#〜省略〜
<%= f.text_field :word %> #削除
#下2行追加
<% tag = Book.select(:tag).distinct %>
<%= f.collection_select(:word, tag, :tag, :tag) %>
#〜省略〜
tag = Book.select(:tag).distinct
本の投稿からtagカラムだけを抜き出し、distinct
で重複を削除
※distinct
を使用しないと下記のように全件表示される(空欄はtag未設定の投稿)
f.collection_select(:word, tag, :id, :tag)
collection_selectはRailsドキュメントを参照すると
Railsドキュメント(collection_select)
f.collection_select(メソッド名, 要素の配列, value属性の項目, テキストの項目, オプション={}, HTML属性={} or イベント属性={})
とある。つまり
- 第1引数
:word
メソッド名。送信したい項目名と言い換えればわかりやすいか - 第2引数
tag
参照するデータ。今回はひとつ上に書いたtag = Book.〜
のtagを指定。Book.all等も指定できる - 第3引数
:tag
第2引数のデータの中で、送信するデータのカラムを指定。tagカラムの内容を送りたいので:tag
- 第4引数
:tag
テキストの項目。第2引数のデータの内実際に選択肢として表示したい物。tagカラムの中身を表示したいので:tag
※この場合下記の様に、コントローラーは完全一致で検索するように修正したほうが的確
@books = Book.where(tag: @word)