今回の実装完了イメージは下記の様な画像です。
その為に、検索したキーワードに一致する文字を強調して表示するためのHelperを作ります。
目次
前提条件
投稿機能がある。
今回で言うと、Postテーブルにtitleとcontentカラムが存在します。
検索機能の実装
まずは、検索機能を実装します。
僕の場合は、ヘッダーに検索窓を埋め込みました。
application.html.erb
<%= form_with url: search_path, local: true, method: :get do |form| %>
<%= form.text_field :keyword, :value => @keyword, placeholder: "キーワードで探す", :required => true %>
<%= button_tag :type => "submit", :name => nil do %>
<i class="fas fa-search"></i>
<% end %>
<% end %>
検索ボタンにはフォントオーサムを使用しています。
表示されない方は、まずはフォントオーサムを表示できる様に設定しましょう。
【爆速】Rails6にフォントオーサム(Font Awesome 5)を導入する方法
posts_controller.rb
def search
@keyword = params[:keyword]
@keywords = @keyword.split(/[[:blank:]]+/)
@keywords.each do |keyword|
@posts = Post.where("title like :q OR content like :q ", q: "%#{keyword}%")
end
@posts = @posts.order(id: :DESC).page(params[:page])
end
これで、空白区切り(半角、全角)で、2語以上の検索もできます。
.page(params[:page])
の部分はkaminariのgemでページネーションを実装しているので、導入していない方は記述しなくて大丈夫です。
search.html.erb
<% if @posts.present? %>
<div class="loop-content-article-ttl">
<h1>「<b><%= params[:keyword] %>」</b>の検索結果<span><%= @posts.total_count %>件</span></h1>
<p><%= @posts.current_page %> / <%= @posts.total_pages %> ページ</p>
</div>
<ul class="loop-content-article-list">
<% @posts.each do |post| %>
<%= render 'l-article', post: post %>
<% end %>
</ul>
<%= paginate @posts %>
<% else %>
<div class="loop-content-article-nosearch">
<p>「<b><%= params[:keyword] %></b>」の検索結果はありませんでした。</p>
<p>キーワードを変更してもう一度検索してください。</p>
</div>
<% end %>
こちらにもkaminariを使用していますので、ページ数の表示やページネーション を入れているタグは、導入していない方は記述しなくて大丈夫です。
_l-article.html.erb
<h2><%= post.title %></h2>
<p><%= post.content.truncate(110).delete '#,`,*,\,' %></p>
出力は他ページにも使いまわしているので、テンプレート化しています。
検索結果で一致したキーワードを強調
ここからが今回の目的の実装です。
posts_helper.rb
module PostsHelper
def emphasize_keyword(content, keyword)
keyword = keyword.split(/[[:blank:]]+/)
content_array = content.split(/\R/)
first_match_line = content_array.find { |e| /#{keyword}/ =~ e }
trancate_line = if first_match_line.present?
truncate(first_match_line, length: 110).delete '#,`,*,\,'
else
truncate(content, length: 110).delete '#,`,*,\,'
end
highlight(trancate_line, keyword, :highlighter => '<span class="keyword-highlight">\1</span>')
end
end
- 検索キーワードに一致する本文を1行を抜き出す
- 抜き出した本文を100文字に省略する
- 検索キーワードに一致する文字を太字にする
上記の実装をhelperにまとめています。
scssは下記の様にしました。
// 検索キーワードハイライト
.keyword-highlight {
background: #fff093;
font-weight: 700;
padding: 2px 3px;
font-style: italic;
}
_l-article.html.erb
あとはviewでhelperを使います。
<% if current_page?(search_path) %>
<h2><%= emphasize_keyword(post.title, @keyword) %></h2>
<% else %>
<h2><%= post.title %></h2>
<% end %>
<% if current_page?(search_path) %>
<p><%= emphasize_keyword(post.content, @keyword) %></p>
<% else %>
<p><%= post.content.truncate(110).delete '#,`,*,\,' %></p>
<% end %>
これで実装完了です!
あとはブラウザで検索してみて、ハイライトされているのを確認してください!