現在のページに応じてactiveクラスを付与する
メニューリストとかで現在のページに応じて色がついてたらサイトが見やすくなりますよね。
Boardsページにいる時
Usersページにいる時
こんな感じにしたいです。
今回はこれを実装していきます。
前回作ったアプリにBootstrapを導入してBoardのCRUD機能をつけておきました。
viewも最低限整えてます。
どう実装するかですが、現在のページに応じて、というところが肝になりそうです。
では何で判断すればいいか。
そのページがどのコントローラを通っているか、を判断できるようなメソッドを作れば実装できそうです。
ヘルパーメソッドを作成
app/helpers/application_helper.rbにヘルパーメソッドを定義します。
module ApplicationHelper def add_active(controller_name) if controller_name == params[:controller] return 'active' end end
もしparamsで受け取っているcontrollerが引数で渡したcontroller_nameと一致していたら、文字列'active'を返す。
というメソッドです。
viewに記述
_menu.html.erb <ul class="nav nav-pills"> <li> <%= link_to 'User List', root_path, class: "nav-link #{add_active('users')}" %> <%# users_controllerを通っていたらadd_activeメソッドを適用 %> </li> <li> <%= link_to 'Board List', boards_path, class: "nav-link #{add_active('boards')}" %> <%# boards_controllerを通っていたらadd_activeメソッドを適用 %> </li> </ul>
こんな感じです。
細かいですが変数展開を使っているのでclassの要素はダブルクォーテーション("")で囲みます。
僕はこれで30分程溶けました。
できました。
ログと検証ツールでも
users_controllerを通っている時
boards_controllerを通っている時
想定通りになりました。
最後に
こういう便利ヘルパーメソッドを作ったり、リファクタリングしたりが苦手です。今回もいろいろ参考にさせてもらいました。引き出しを増やして最適なものを組み合わせる、という方向でも伸ばしたいです。
では今回は以上です。ありがとうございました。
参考サイト
ransackの検索機能
今回はgem ransack
で検索機能を実装します。
以前の記事でフリーワード検索を実装したのですが、今回は作成日での検索とセレクトボックスでの検索を実装していきます。
1つ前の記事で作ったscaffoldアプリを元にやっていきます。
このアプリではi18n、enum、enum_helpを導入済みです。
ではいってみましょう。
ransackをインストール
gemfileに
gem 'ransack'
として
bundle install
します。
コントローラー編集
コントローラーで検索結果を受け取れるようにします。
def index # @users = User.all これを削除 @q = User.ransack(params[:q]) #この2行を @users = @q.result(distinct: true) #追加します end
ransackメソッド
送られてきたパラメーターを元にテーブルからデータを検索するメソッド
(params[:q])
検索パラメータを取得
resultメソッド
ransackメソッドで取得したデータをオブジェクトに変換するメソッド
(distinct: true)
検索結果の重複を取り除く
viewの編集
ついでにviewをちょっと見やすく編集します。
users/index.html.erb <p id="notice"><%= notice %></p> <h1>Users</h1> <table> <thead> <tr> <th>Name</th> <th>Gender</th> <th>Created_at</th> <th colspan="3"></th> </tr> </thead> <tbody> <%= render @users %> </tbody> </table> <br> <%= link_to 'New User', new_user_path %>
users/_user.html.erb <tr> <td><%= user.name %></td> <td><%= user.gender_i18n %></td> <td><%= user.created_at %></td> <td><%= link_to 'Show', user %></td> <td><%= link_to 'Edit', edit_user_path(user) %></td> <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr>
ユーザー情報部分を部分テンプレートとして切り出して、作成日時も表示されるようにしました。
検索フォームの作成
セレクトボックスで検索
まずはセレクトボックスで、性別で検索できるようにします。
users/index.html.erb <p id="notice"><%= notice %></p> <h1>Users</h1> <%= search_form_for @q do |f| %> <%= f.select :gender_eq, User.genders_i18n.invert.map{|key, value| [key, User.genders[value]]} %> <%= f.submit %> <% end %> <table> <thead> <tr> <th>Name</th> <th>Gender</th> <th>Created_at</th> <th colspan="3"></th> </tr> </thead> <tbody> <%= render @users %> </tbody> </table> <br> <%= link_to 'New User', new_user_path %>
search_form_for
ransackが提供するメソッド
f.selectのところがややこしいので1つ1つ見ていきます。
まず第一引数の:gender_eq
ですが、選択した性別と等しいものを探すという意味です。eqはイコール(equal)の略ですね。
そしてややこしい第二引数。
User.genders_i18n.invertまでは前回の記事を参照してください。
irb(main):001:0> User.genders_i18n.invert => {"男性"=>"male", "女性"=>"female", "秘密"=>"secret"} irb(main):002:0> User.genders_i18n.invert.map{|key,value|[key,User.genders[value]]} => [["男性", 0], ["女性", 1], ["秘密", 2]]
この2つを比べてみます。
違いは
- ハッシュから配列に変わっている
- male, female, secretから0, 1, 2に変わっている
の2点、違いがあります。
ハッシュから配列に変わっているのはmapメソッド
によるものです。
mapメソッドは、配列orハッシュ.map {|変数名| 実行する処理 }
で戻り値を配列で返します。
今回のややこしいところに当てはめてみると、
irb(main):001:0> User.genders_i18n.invert => {"男性"=>"male", "女性"=>"female", "秘密"=>"secret"}
これを配列の形になおして欲しい。
配列の中身は|key, value|という形にしてくれ。
keyはそのままkey("男性"=>"male"の"男性")、
valueはUser.genders[value]つまり
irb(main):003:0> User.genders => {"male"=>0, "female"=>1, "secret"=>2}
のvalueの部分("male"=>0の0)
つまり最終的な形が
irb(main):002:0> User.genders_i18n.invert.map{|key,value|[key,User.genders[value]]} => [["男性", 0], ["女性", 1], ["秘密", 2]]
になる、という流れです。
これでセレクトボックスによる検索が実装できました。
最後に指定なしでも検索できるように
<%= f.select :gender_eq, User.genders_i18n.invert.map{|key, value| [key, User.genders[value]]}, include_blank: '指定なし' %>
include_blank
を設定しておきます。
複数条件で検索する時に必要です。
作成日で検索
次は作成日での検索です。
例えば5/1の00:00から5/10の23:59で検索したいとします。
<%= search_form_for @q do |f| %> <%= f.select :gender_eq, User.genders_i18n.invert.map{|key, value| [key, User.genders[value]]}, include_blank: '指定なし' %> <%= f.date_field :created_at_gteq %> <span>〜</span> <%= f.date_field :created_at_lteq %> <%= f.submit %> <% end %>
date_field
日付の入力欄を生成
gteq
greater than equalの略
lteq
less than equalの略
created_at_gteq
で指定した日付以降
created_at_lteq
で指定した日付まで
こんな感じで日付で検索できます。
しかし実際に検索してみると
5/10の15:00:00に作成したyamadaさんが表示されていません。
created_at_lteqだと、その日の00:00までの範囲で検索してしまうからです。
これを23:59までにするにはpredicate
のカスタマイズが必要です。
predicateのカスタマイズ
まずconfig/initializers/ransack.rbを作成します。
Ransack.configure do |config| config.add_predicate 'lteq_end_of_day', # 名前を付ける arel_predicate: 'lteq', # 'lteq'をカスタマイズしますよ formatter: proc { |v| v.end_of_day } # end_of_dayメソッドを実行 end
これでカスタマイズできたので検索フォームを修正してみます。
<%= search_form_for @q do |f| %> <%= f.select :gender_eq, User.genders_i18n.invert.map{|key, value| [key, User.genders[value]]}, include_blank: '指定なし' %> <%= f.date_field :created_at_gteq %> <span>〜</span> <%= f.date_field :created_at_lteq_end_of_day %> <%= f.submit %> <% end %>
これで
想定通りの検索ができました。
最後に
自分的には難しい内容でした。コンソールを使ったりして流れを追って、なんとかやってることはわかった、ぐらいの感じです。
このロジックを自分で考えるのはちょっと今の理解度では厳しいな、と思いました。アウトプットを続けて知識を定着させていきたいです。
あと参考にしたサイトとかいつも載せてなかったんですが載せないとダメですよね、忘れてました。すいません。
今日は疲れたので次回から載せます…
最後まで読んでいただきありがとうございました。間違いなどありましたらコメントいただけますと幸いです。
enum_help導入
制作中の掲示板アプリにenum_helpを導入しました。
ちゃんと定着させるためにもう一度実際に手を動かしながらやってみようと思います。
今回やりたいのは
フォームにセレクトボックスを作り選択肢を日本語で表示する
です。
こんな感じの作ります。
i18n、enum、enum_helpを使って実装したいと思います。
rails newからやったので順番にいきましょう。
新規アプリ作成
rails new sample_app
名前は適当にsample_appで作成します。
cd sample_app
で移動して
rails g scaffold user name:string gender:integer
scaffoldで雛形を作り、userモデルを作成。カラムは名前と性別を作ります。
性別(gender)はinteger型
にしておきます。
enum用のカラムはinteger型かboolean型かのどちらかです。
integerなら整数、booleanなら真偽値(true, false)でDBに保存されます。
選択肢が2択ならboolean、それより多い、今後増える、とかの場合はintegerがいいのかなと思います。
マイグレーションファイルを
class CreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| t.string :name, null: false t.integer :gender, default: 0, null: false t.timestamps end end end
このように編集して
rails db:migrate
します。
gemのインストール
必要なgemをインストールしましょう。
gemfileに
gem 'rails-i18n' gem 'enum' gem 'enum_help'
として
bundle install
これでインストールできたかと思います。
enumの定義
userモデルでenumの定義をします。
# user.rb enum gender: { male: 0, female: 1, secret: 2 }
i18nの設定
config/application.rbに必要な設定を記述します。
require_relative 'boot' require 'rails/all' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) module SampleApp class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.2 # Settings in config/environments/* take precedence over those specified here. # Application configuration can go into files in config/initializers # -- all .rb files in that directory are automatically loaded after loading # the framework and any gems in your application. # これを追加 config.i18n.default_locale = :ja #デフォルト言語を日本語に設定 end end
config/locales/ja.ymlを作成し、
ja: enums: user: gender: male: 男性 female: 女性 secret: 秘密
翻訳情報を作成します。
viewを整形
viewを整えます。
users/_form.html.erb <%= form_with(model: user, local: true) do |form| %> <% if user.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2> <ul> <% user.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div class="field"> <%= form.label :name %> <%= form.text_field :name %> </div> <div class="field"> <%= form.label :gender %> <%= form.number_field :gender %> <%#この行を削除 %> <%= form.select :gender, User.genders_i18n.invert %> <%# この行を追加 %> </div> <div class="actions"> <%= form.submit %> </div> <% end %>
User.gendersでuserモデルのgenderの値をハッシュで取得します。
irb(main):001:0> User.genders => {"male"=>0, "female"=>1, "secret"=>2}
これをUser.genders_i18nとすると
irb(main):002:0> User.genders_i18n => {"male"=>"男性", "female"=>"女性", "secret"=>"秘密"}
ja.ymlファイルで設定した日本語が適用されました。
このカラム名(複数形)_i18n
がenum_helpで使えるようになります。
しかしこれだと
選択肢は英語のままになっています。
ハッシュのkeyの方が取得されていますね。これに
irb(main):003:0> User.genders_i18n.invert => {"男性"=>"male", "女性"=>"female", "秘密"=>"secret"}
invert
メソッドを付けるとkeyとvalueを入れ替えることができます。
これで再度見てみると
日本語になりました。
最後に
無事実装できました。使用頻度が高そうな気がするのでしっかり押さえたいです。
以上となります。読んでいただきありがとうございました。
Admin-LTEで管理者用ページを実装
今回はAdmin-LTE
を使って管理者用ページを作りたいと思います。
Admin-LTEとは管理画面に特化したBootstrapベースのCSSフレームワークです。
管理者用なので凝ったデザインにする必要はないですよね。なのでフレームワークを使ってサクッと作ってしまいたいです。
インストール・設定
yarnでインストールしました。
yarn add admin-lte
インストールするとnode_modules
というディレクトリが作成されます。
今回はこの中のadmin-lte/starter.html
を見本に作ってみます。
Admin-LTEのページでいろんなテンプレートが見れるのでここから好みのデザインを選ぶのもいいと思います。
Demoでテンプレートを開いて検証ツールを開きます。
CSSはhead
タグの中
JavaScriptはbody
タグの中
にあるので、app/assets内で必要なものを読み込みます。
管理者用ページとユーザーが使うページのデザインが異なるのでapplication.jsやapplication.scssではなく、新たに管理者用ページのマニフェストファイルadmin.js
とadmin.scss
を作成し、そこに記述します。
// app/assets/javascript/admin.js //= require jquery3 //= require rails-ujs //= require admin-lte/plugins/bootstrap/js/bootstrap.bundle.min.js //= require admin-lte/dist/js/adminlte.min.js
/* app/assets/stylesheets/admin.scss */ @import 'font-awesome-sprockets'; @import 'font-awesome'; @import 'admin-lte/plugins/fontawesome-free/css/all.min.css'; @import 'admin-lte/dist/css/adminlte.min.css';
application.js内の//= require_tree .
は全てのjsファイルを読み込んでしまうので、view毎にデザインを変えたい場合は削除します。
また、application.js、application.scssはデフォルトで読み込んでくれますが、それ以外のマニフェストファイルは設定しないといけないので
# config/initializers/assets.rb # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in the app/assets # folder are already added. Rails.application.config.assets.precompile += %w[admin.js admin.css] # コメントアウトを外す
とします。
Controllerを作成
管理機能を持つコントローラを作ります。
管理用に階層を分けた方が管理しやすそうです。
rails g controller Admin::Base
でApplicationControllerを継承したAdmin::BaseControllerを作成し、Admin系コントローラの基幹とします。
さらにこのAdmin::BaseControllerを継承したコントローラで管理者ページの機能を作ります。
階層はこんな感じです。
admin/user_sessions_controller.rbとadmin/dashboards_controller.rbは
class Admin::UserSessionsController < Admin::BaseController 略 end
class Admin::DashboardsController < Admin::BaseController 略 end
として基幹となるAdmin::BaseControllerを継承します。
また、一般ユーザー用のページとは異なるデザインを当てるため、
class Admin::BaseController < ApplicationController layout 'admin/layouts/application' 略 end
layoutメソッド
で使用するlayoutファイルを指定します。
Viewを作成
viewの中身は省略しますが、前述の通りnode_modules/admin-lte/starter.html
をコピペして不要な部分を削除しました。
layoutファイルでは
<%= stylesheet_link_tag 'admin', media: 'all' %> <%= javascript_include_tag 'admin' %>
読み込むCSSとJavaScriptをadmin
と指定します。
ルーティングの設定
管理者用ページのpathはadmin/login
のようにしたいので
namespace :admin do root 'dashboards#index' get 'login', to: 'user_sessions#new' post 'login', to: 'user_sessions#create' delete 'logout', to: 'user_sessions#destroy' end
namespace
を使います。すると
ルーティングは/admin/○○
のようになります。
enumの定義・管理者かどうかの判別をするカラムを作成
enum
とは簡単に言うと名前に数値を割り当てることです。
user.rbでUserモデルにenumの定義をしておきます。
enum role: { general: 0, admin: 1 }
これだけです。
Userモデルで管理者かそうでないかの判別をするrole
カラムを作ります。roleとは役割という意味です。
rails g migration add_role_to_users
で作られたマイグレーションファイルに
class AddRoleToUsers < ActiveRecord::Migration[5.2] def change add_column :users, :role, :integer, default: 0, null: false end end
としてrails db:migrate
します。
enumの定義で数字を割り当てたので型はintegerです。
一般か管理者か、必ずどちらか割り当てるためnull: false
デフォルトは一般にしたいのでdefault: 0
とします。
僕はnullとdefaultのオプションを付けずに作ってしまって、後から
rails g migration change_column_to_users
class ChangeColumnToUsers < ActiveRecord::Migration[5.2] def change change_column_null :users, :role, false change_column_default :users, :role, from: nil, to: "0" end end
としようとしたらエラーが出て追加できませんでした。
レコードを全削除したら無事追加できたんですが良い手ではないのでまた調べたいです。
まだ機能が全然ないですがとりあえず管理者用ページができました。
最後に
change_columnできなかったところや管理ツール(yarnなど)のこと、アセットパイプラインなどやればやるほど分からないことが出てきて沼です。
ブログのネタができて嬉しいなぁ…1つ1つ、潰していきたいです。
以上です、ありがとうございました。
パスワードリセット機能の実装
ユーザー登録ができるサービスには必ず付いていると言っても過言ではないであろう機能、パスワードリセット機能を実装していきます。
今回はgem sorcery
のモジュールであるreset_password
を導入します。sorceryの導入を前提で進めていきます。
パスワードリセットの流れ
- パスワードリセット申請ページからメールアドレスを送信
- メールを受信しリンクをクリック
- パスワードリセットページでパスワードを更新
という感じです。
開発環境ではメールを実際には送らないようletter_opener_web
というgemを使ってますがそちらは割愛させていただきます。
にではいってみましょう。
reset_passwordモジュールのインストール
$ rails g sorcery:install reset_password --only-submodules
でインストール。
マイグレーションファイルが作成されます。
トークンは一意でなければいけないのでreset_password_tokenカラムにindex制約をかけておきます。
トークンとはワンタイムパスワードのようなものです。
class SorceryResetPassword < ActiveRecord::Migration[5.2] def change add_column :users, :reset_password_token, :string, default: nil add_column :users, :reset_password_token_expires_at, :datetime, default: nil add_column :users, :reset_password_email_sent_at, :datetime, default: nil add_index :users, :reset_password_token # これを追加 end end
として
rails db:migrate
DBに反映。
モデルの方でもvalidationを設定しておきます。
# user.rb validates :reset_password_token, uniqueness: true, allow_nil: true
allow_nilオプション
は、対象の値がnilの場合にバリデーションをスキップします。
パスワードが変更される際にreset_password_tokenは削除されnilになります。
allow_nil: trueがないとユニーク制約にに引っかかってしまうのでこちらも設定します。
Mailerの作成
メイラーとはRailsからメールを送信する機能です。
まずは
$ rails g mailer UserMailer reset_password_email
でUserMailerを作成します。
rails g mailer メーラー名 メソッド名
です。
共通の設定
# application_mailer.rb class ApplicationMailer < ActionMailer::Base default from: 'example@example.com' # メールの送信元 layout 'mailer' # mailer.html.erb、mailer.text.erbをメールのレイアウトとして使用する end
と
メール送信のメソッドを定義します。
# user_mailer.rb class UserMailer < ApplicationMailer def reset_password_email(user) @user = User.find(user.id) @url = edit_password_reset_url(@user.reset_password_token) mail to: user.email, subject: 'パスワードリセット' end end
mailメソッド
で送信先、メールのタイトルを設定しています。
sorcery.rbの設定もしておきます。
Rails.application.config.sorcery.submodules = [:reset_password] # サブモジュールとしてreset_passwordを設定 Rails.application.config.sorcery.configure do |config| config.user_config do |user| user.reset_password_mailer = UserMailer # パスワードリセット用のメーラーにUserMailerを指定 end end
メール本文を作成
views/user_mailer/reset_password_email.text.erb
とviews/user_mailer/reset_password_email.html.erb
に実際のメールの内容を記述します。
一部のメールソフトではhtml形式のメールが受け取れない(ガラケーとか?)などがあるようで、その対策としてtext形式とhtml形式の2つで作成します。
Railsではマルチパートメールという仕組みでtext形式とhtml形式の両方をメール送信して、メールクライアント側で自動判別してメールを表示してくれるそうです。
また、メンテナンス等の際、両方ともメンテしなくてもいいようにactionmailer-text
というgemもあるようです。
ここでは取り扱わないので興味のある方は調べてみてください。
コントローラーの設定
# app/controllers/password_resets_controller.rb class PasswordResetsController < ApplicationController skip_before_action :require_login def create @user = User.find_by_email(params[:email]) @user.deliver_reset_password_instructions! if @user redirect_to(root_path, :notice => 'Instructions have been sent to your email.') end def edit @token = params[:id] @user = User.load_from_reset_password_token(params[:id]) if @user.blank? not_authenticated return end end def update @token = params[:id] @user = User.load_from_reset_password_token(params[:id]) if @user.blank? not_authenticated return end @user.password_confirmation = params[:user][:password_confirmation] if @user.change_password(params[:user][:password]) redirect_to(root_path, :notice => 'Password was successfully updated.') else render :action => "edit" end end end
フォームの作成
パスワードリセット申請フォーム
<%= form_with url: password_resets_path, local: true do |f| %> <%= f.label :email, 'メールアドレス' %> <%= f.email_field :email, class: 'form-control' %> <%= f.submit class: 'btn btn-primary' %> <% end %>
パスワードリセットフォーム
<%= form_with model: @user, url: password_reset_path(@token), local: true do |f| %> <%= f.label :email %><br> <%= @user.email %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit class: 'btn btn-primary ' %> <% end %>
ルーティングの設定
resources :password_resets, only: %i[create edit update]
を追加。 以上です。
最後に
今まで使ったことなかったMailerとかが出てきて難しかったです。
githubからコピペしたのでコントローラ内のコードの流れが正直まだわかってません。
また振り返って流れを追いたいと思います。
間違いなどありましたらコメントいただけると幸いです。
読んでいただきありがとうございました。
form_withと仲良くなろう
form_withってよく使うけど曖昧なままきてるなあ、と思ったので振り返ってみます。以前にも一度記事にしたので2回目です。
色々やってくれる優秀なform_withですが、model渡したりurl渡したり、結局どんな動きしてるの?
form_withとは
情報を送信するためのヘルパーメソッドです。
urlを渡す
url
はフォームの情報の送信先のURLを指定するためのオプションです。
urlを渡すと、そのpathに対してデフォルトではPOSTメソッドでリクエストが送られます。
<!-- ログインフォーム --> <%= form_with url: login_path, local: true do |f| %> <%= f.label :email %> <%= f.text_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.submit class: 'btn btn-primary' %> <% end %>
ログインの情報はユーザー照合するだけでDBに保存しないのでmodelは渡しません。(と思ってるのですが自信ないので間違ってたらご指摘ください。)
modelを渡す
model
はDBに保存する時に指定します。
<!-- ユーザー登録、更新フォーム --> <%= form_with model: @user, local: true do |f| %> <%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> <%= f.label :email %> <%= f.text_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit class: 'btn btn-primary' %> <% end %>
modelに指定している@user
はコントローラで定義したインスタンスです。
このインスタンスが
空 か 値が入ってるか
で処理が変わります。
空の場合は
def new @user = User.new. # 空のインスタンスを作成 end def create @user.create(user_params) end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end
空ってことは新規作成したいんだな
↓
createアクション
値が既に入ってる場合は
def edit @user = User.find(params[:id]) # ユーザー情報をfindで取得して@userに代入 end def update @user.update(user_params) end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end
値が既に入ってるから更新したいんだな
↓
updateアクション
という感じで判断してくれます。
最後に
form_with然り、render然り、redirect_to然り、Railsはその「よしなに力」の高さ故、初学者にとっては逆に何をやってるのかわからない、となりがちだと思います。
なんか動いてるラッキー、ではなく何が省略されてるのか、どんな処理が走っているのか、しっかり理解しながら進めていきたいですね。
ransackで検索フォームを実装
今回は検索フォームを実装したいと思います。
こちらが完成品です。
掲示板のタイトルか本文にワードが含まれていたら検索結果を一覧で表示させるようにします。
ransack
ransackとは簡単に検索機能を実装できるgemです。
インストール
まずはGemfileに
gem 'ransack'
を加えてbundle install
します。
コントローラーを編集
class BoardsController < ApplicationController def index @q = Board.ransack(params[:q]) @boards = @q.result(distinct: true) end
ransackメソッド
送られてきたパラメーターを元にテーブルからデータを検索するメソッド
params[:q])
検索パラメータを取得
resultメソッド
ransackメソッドで取得したデータをオブジェクトに変換するメソッド
(distinct: true)
検索結果の重複を取り除く
ビューを作成
検索フォームは他のページにも使いたいので部分テンプレート化しておきます。
<!-- _search_form.html.erb --> <%= search_form_for q, url: url do |f| %> <div class='input-group'> <%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: '検索ワード', type: 'search' %> <div class='input-group-append'> <%= f.submit class: 'btn btn-primary' %> </div> </div> <% end %>
ransackで用意されているsearch_form_for
でフォームを作りましょう。
フォーム部分はf.search_field
とすることで検索用のフォームとして作ることができ、type='search'が付与されます。
こうすることでフォームにxボタンが付き、表示内容を削除できます。
あとは表示させたいところでrenderしてあげるだけです。
<!-- app/views/boards/index.html.erb --> <%= render 'search_form', q: @q, url: boards_path %>
q:@q
とurl: url
の部分は、ローカル変数に値を渡すことで汎用的に使えるようにしています。
_cont
もransackで用意されているメソッドで、検索したワードが含まれているレコードを取得するものです。
title_or_body_contのように_or_
で繋ぐことでtitleカラムとbodyカラムにワードが含まれているか検索してくれます。
検索の仕方は他にも色々メソッドがありそれぞれ出来ることが違うので気になる方は調べてみてください。
最後に
検索機能はほぼ必須というくらい、どんなサービスでも見かけるものだと思います。
複雑なものになってくるとちょっとどうかわかりませんが、これくらいの検索機能ならransackを使えば簡単に実装できますね。
この記事が参考になれば幸いです。