progateからrailsチュートリアル一周というよくある感じの勉強をしているものです。
なにかアウトプットも重要だと思いrailsチュートリアルでの拡張機能で提案されているユーザー名での検索機能を作成しその間の初心者なりの思考を言語化して残して置こうと考えこの記事を書きました。
はじめに
ユーザー名での検索機能とはどのような流れだろうか?を考え
1. 検索するユーザー名をユーザーさんから受け取り
2. そのデータをもとにusersテーブルからnameで検索をかけて@usersにいれる
3. 検索用のviewつくるのは面倒なのでviewはrailsチュートリアルで作ったindex.html.erbを再利用して検索結果を表示
以上の三点を考えました。このすごくざっくりした考えに沿って機能を追加しようと思います。
(本当にこんな感じでスマホのメモ帳に書きなぐってましたw)
本当に簡素な機能の追加ですが、なるだけコピペではなく内容を理解しながら書こうと思いコードの意味をざっくりですがメモしてあります。実際にこのコードを書くときはガンガンググってるんですけどね。
1.検索するユーザー名をユーザーさんから受け取り
受け取るにはフォームをつくればok 俺でもわかる
<%= form_tag search_path, :method => 'get' do %>
<p>
<%= text_field_tag :search_name,params[:search_name], class: 'form-control'%>
<%= submit_tag "Search"%>
</p>
<% end%>
モデルに依存するものではないと思ったのでform_tag、DBに入力したり変更加えたりするものではないのでGETメソッド
text_field_tag 一つの引数が要素の名前、2つ目が値
パーシャルにしてもともとあるページに埋め込むのが一番簡単かなと考えindex.htmlに記述
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= render 'search/search_name_form' %>
<%= will_paginate %>
<ul class="users">
<%= render @users %>
</ul>
<%= will_paginate %>
2.そのデータをもとにusersテーブルからnameで検索をかけて@usersにいれる
コントローラにwhereとか逐一書くよりUserが検索できるメソッドを持っている方がオブジェクト指向っぽいなと思ったので
Userにメソッドを追加
class User < ApplicationRecord
def self.search(name)
where('name LIKE ?',"%#{name}%")
end
selfを加えないとインスタンスメソッドになってしまう(user = User.newで定義したuserとかでしか使えない)ので入れる
LIKEであいまい検索ができる
?として値を直接渡さないのはSQLを直接いじれるようにするのはセキュリティ的にまずいので
次に実際にコントローラーにアクションをつくってviewに値を渡す
なにか動作加えるならコントローラーを新たに作ろうということrails g controller search
(ここが一番ツッコミどころかもしれない Userscontrollerに記述するほうが良かったかも
一番「なぜそうしたか」 が説明できない)(追記 Userscontrollerにsearchアクションをつくるべきでしたね)
class SearchController < ApplicationController
def search
@users = User.search(params[:search_name]).paginate(page: params[:page], per_page: 5)
render 'users/index'
end
アクションを定義して@usersにフォームから受け取ったデータを使って検索したデータを入れます。searchに引数としてparams[:search_name]を入れて、index.html.erbを再利用するためにpaginateを追加しています。
そして新たなアクションを定義したのでroutsも追加
get 'search', to:'search#search'
ここなんか汚いですね 名前もう少し気を利かせたほうが良かったかも
3.検索結果のviewつくるのは面倒なのでviewはrailsチュートリアルで作ったindex.html.erbを再利用して検索結果を表示
render 'users/index' でviewを再利用するのですが
ここで一つ困ったことが
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= render 'search/search_name_form' %>
<%= will_paginate %>
<ul class="users">
<%= render @users%>
</ul>
<%= will_paginate%>
のようにrailsチュートリアルのままでのindex.html.erbを使うとpaginateに有効なやつが入ってないよ!とエラーがでます。
少しググってみると今までwill_paginateはリソースからいい感じに引数をデフォルトで変えていてくれていたみたいです。
(今まではresources :usersでアクセスしていたので@usersがデフォルトとなっていた。)
今回はリソースを使ってアクセスしていないので指定する必要があります
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= render 'search/search_name_form' %>
<%= will_paginate %>
<ul class="users">
<%= render @users%>
</ul>
<%= will_paginate%>
こうすることでリソースを通したアクセスでなくてもwill_paginateを使うことができました。新しくviewをつくるハメにならなくてよかったです。
おわりに
自分なりに言語化するとあやふやな部分がわかりますね。rubyではなくruby on railsを勉強しているような感じになっていますが、実際に自分の考えたようにブラウザで動作してくれるとやはり楽しいです。
それにしてもrailsチュートリアルを終えてもまだまだ勉強することが多いですね。むしろ進めれば進めるほど勉強すべきことが増えてる気がします。プログラミングの世界は奥が深い。
なにか指摘等ありましたらコメントしていただけると嬉しいです。
ここまで読んでくださってありがとうございました。