新米パパの育児留学

新米パパの育児留学

『育児留学』とは、育児を通して異なる視点を得たり新しいことに挑戦して自己成長に繋げること。育児奮闘中の新米パパが育児を通して得た気づきや感じたこと、育休中に習得したプログラミングに関する話題を発信していきます。

Ruby on Rails 検索機能拡張 (railsチュートリアル)

目的

Ruby on Rails チュートリアル 5.0(第4版)で実装したサンプルアプリケーションを元に、検索機能を拡張します。

完成版アプリケーション

実際に検索機能を拡張したアプリケーションを見てみたい方はこちら。

https://fathomless-shore-36670.herokuapp.com/about

開発環境

Rails 5.0

・cloud9

実装

1.indexページにユーザ検索機能を実装する

最終的なソースコードと確認画面

[View]

[users/index.html.erb]

<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>

<!--  検索拡張機能  -->
<p>user search</p>
<%= form_tag(users_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search', :name => nil %>
<% end %>

<ul class="users">
  <%= render @users %>
</ul>
<%= will_paginate %>

[Controller]

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def index
    @users = User.where(activated: true).paginate(page: params[:page]).search(params[:search])
  end
(中略)
end

[Model]

[models/user.rb]

class User < ApplicationRecord
(中略)
  def self.search(search) #ここでのself.はUser.を意味する
    if search
      where(['name LIKE ?', "%#{search}%"]) #検索とnameの部分一致を表示。User.は省略
    else
      all #全て表示。User.は省略
    end
  end
  
  private
(中略)
end

[確認画面]

[検索前] f:id:mochikichi321:20170326111010p:plain

[検索後] f:id:mochikichi321:20170326111015p:plain

実装ステップ

手順を知りたい方向け。

STEP1

まず、現時点でのindexページ及びusers_controllerはこのようになっている。

[users/index.html.erb]

<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>

#ここへ検索拡張機能追加予定

<ul class="users">
  <%= render @users %>
</ul>
<%= will_paginate %>

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def index
    @users = User.where(activated: true,).paginate(page: params[:page])
  end
(中略)
end

f:id:mochikichi321:20170326111218p:plain

STEP2

フォームを作成。パスはusers#indexの"users_path"を指定。 text_field_tagのシンボルは任意なので":search"とする。

[users/index.html.erb]

<%= form_tag(users_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search' %>
<% end %>

submitボタンを押すとエラーが出る。 f:id:mochikichi321:20170326111415p:plain

STEP3

ブラウザからソースコードを確認すると、以下の通りとなっており、method=“post"となっていることがわかる。

<form action="/users" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input type="hidden" name="authenticity_token" value="ncc66/h8qa7NC/78Rxyk0UA0UdCNhOReJockKrID+IkNAjgA9gORJ+1KwJiPTT7kRS7zvELfwm1ZeH0gLmSa/g==" />
  <input type="text" name="search" id="search" />
  <input type="submit" name="commit" value="Search" data-disable-with="Search" />
</form>

STEP4

method=“get"へ指定。

[users/index.html.erb]

<%= form_tag(users_path, method: :get) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search' %>
<% end %>

STEP5

users_controllerのindexアクションにsearchを追加。

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def index
    #条件分岐
    @users = if params[:search]
      #searchされた場合は、原文+.where('name LIKE ?', "%#{params[:search]}%")を実行
      User.where(activated: true).paginate(page: params[:page]).where('name LIKE ?', "%#{params[:search]}%")
    else
      #searchされていない場合は、原文そのまま
      User.where(activated: true).paginate(page: params[:page])
    end
  end
(中略)
end

これで検索機能は実装された。

STEP6

searchフォームに:searchを加える。

[users/index.html.erb]

<%= form_tag(users_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search' %>
<% end %>

STEP7

URLに以下が含まれるが消す。

“commit=Search”

[users/index.html.erb]

<%= form_tag(users_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search', :name => nil %>
<% end %>

STEP8

リファクタリングし、モデルへ移行。

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def index
    @users = User.where(activated: true).paginate(page: params[:page]).search(params[:search])
  end
(中略)
end

[models/user.rb]

class User < ApplicationRecord
(中略)
  def self.search(search) #ここでのself.はUser.を意味する
    if search
      where(['name LIKE ?', "%#{search}%"]) #検索とnameの部分一致を表示。#User.は省略
    else
      all #全て表示。#User.は省略
    end
  end
  
  private
(中略)
end

STEP9

何の検索ができるのかをわかりやすくするために"user search"をform上部に追加

[users/index.html.erb]

<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>

<!--  検索拡張機能  -->
<p>user search</p>
<%= form_tag(users_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search', :name => nil %>
<% end %>

2.userプロフィールページにマイクロポスト検索機能を実装する

最終的なソースコードと確認画面

[View]

[users/show.html.erb]

<% provide(:title, @user.name) %>
<div class="row">
(中略)
  <div class="col-md-8">
    <%= render 'follow_form' if logged_in? %>
    <% if @user.microposts.any? %>
      <h3>Microposts (<%= @user.microposts.count %>)</h3>

      <!--検索拡張機能  -->
      <p>content serch</p>
      <%= form_tag user_path, :method => 'get' do %>
        <p>
          <%= text_field_tag :search, params[:search] %>
          <%= submit_tag "Search", :name => nil %>
        </p>
      <% end %>
      
      <ol class="microposts">
        <%= render @microposts %>
      </ol>
      <%= will_paginate @microposts %>
    <% end %>
  </div>
</div>

[Controller]

[users_controller.rb]

class UsersController < ApplicationController
(中略)
  def show
    @user = User.find(params[:id])
    # 検索拡張機能として.search(params[:search])を追加    
    @microposts = @user.microposts.paginate(page: params[:page]).search(params[:search])
  end
(中略)
end

[Model]

[models/micropost.rb]

class Micropost < ApplicationRecord
(中略)
  def self.search(search) #ここでのself.はMicropost.を意味する
    if search
      where(['content LIKE ?', "%#{search}%"]) #検索とcontentの部分一致を表示。Micropost.は省略。
    else
      all #全て表示。Micropost.は省略。
    end
  end

  private
(中略)
end

[確認画面]

[検索前] f:id:mochikichi321:20170326111930p:plain

[検索後] f:id:mochikichi321:20170326111936p:plain

実装ステップ

基本的には「1.indexページにユーザ検索機能を実装する」と同様のステップ。 [Model]のwhere([‘content LIKE ?’, “%#{search}%”])が"name"から"content"になっている点に注意。

3.Homeページにマイクロポスト検索機能を実装する

最終的なソースコードと確認画面

[View]

[static_pages/home.html.erb]

<% if logged_in? %>
  <%= render 'static_pages/user_logged_in' %>
<% else %>
  <%= render 'static_pages/user_not_logged_in' %>
<% end %>

[static_pages/_user_logged_in.html.erb]

<div class="row">
(中略)
  <div class="col-md-8">
    <h3>Micropost Feed</h3>

    <!--検索拡張機能  -->
    <p>content serch</p>
    <%= form_tag root_path, :method => 'get' do %>
      <p>
        <%= text_field_tag :search, params[:search] %>
        <%= submit_tag "Search", :name => nil %>
      </p>
    <% end %> 
    <%= render 'shared/feed' %>
  </div>
</div>

[Controller]

[static_pages_controller.rb]

class StaticPagesController < ApplicationController
  def home
    if logged_in?
      @micropost  = current_user.microposts.build
      # 検索拡張機能として.search(params[:search])を追加 
      @feed_items = current_user.feed.paginate(page: params[:page]).search(params[:search])
    end
  end
(中略)
end

[Model]

「2.userプロフィールページにマイクロポスト検索機能を実装する」から変更なし。

[models/micropost.rb]

class Micropost < ApplicationRecord
(中略)
  def self.search(search) #ここでのself.はMicropost.を意味する
    if search
      where(['content LIKE ?', "%#{search}%"]) #検索とcontentの部分一致を表示。Micropost.は省略。
    else
      all #全て表示。Micropost.は省略。
    end
  end

  private
(中略)
end

[確認画面]

[検索前] f:id:mochikichi321:20170326112226p:plain

[検索後] f:id:mochikichi321:20170326112233p:plain

実装ステップ

基本的には「1.indexページにユーザ検索機能を実装する」と同様のステップ。

4.リファクタリング

上記で検索機能を3つのviewページへ追加しました。 共通する部分が多いのでリファクタリングします。

[users/_search.html.erb]

<p><%= yield(:search_name) %></p>
<%= form_tag(yield(:path), method: :get) do %>
  <%= text_field_tag :search, params[:search] %>
  <%= submit_tag 'Search', :name => nil %>
<% end %>

[users/index.html.erb]

<% provide(:title, 'All users') %>
<% provide(:search_name, 'user search') %>
<% provide(:path, users_path) %>
<h1>All users</h1>

<%= will_paginate %>

<ul class="users">
  <%= render 'users/search' %>  <-- searchパーシャル呼出 -->
  <%= render @users %>
</ul>

<%= will_paginate %>

[users/show.html.erb]
<% provide(:title, @user.name) %>
<% provide(:search_name, 'content search') %>
<% provide(:path, user_path) %>
<div class="row">
(中略)
  <div class="col-md-8">
    <%= render 'follow_form' if logged_in? %>
    <% if @user.microposts.any? %>
      <h3>Microposts (<%= @user.microposts.count %>)</h3>
      <%= render 'users/search' %>   <-- searchパーシャル呼出 -->
      <ol class="microposts">
        <%= render @microposts %>
      </ol>
      <%= will_paginate @microposts %>
    <% end %>
  </div>
</div>

[static_pages/_user_logged_in.html.erb]

<% provide(:search_name, 'content search') %>
<% provide(:path, root_path) %>
  <div class="row">
(中略)
    <div class="col-md-8">
      <h3>Micropost Feed</h3>
      <%= render 'users/search' %>  <-- searchパーシャル呼出 -->  
      <%= render 'shared/feed' %>
    </div>
  </div>

参考

Simple Search Form in Rails 5

関連記事

mochikichi.hatenablog.com

mochikichi.hatenablog.com

広告を非表示にする