記事について
備忘録的な感じ。前の記事の続きです。Railsは学び始めて11日目です。
雑記的な感じなので結構雑に書いています。でも自分がつまづいたところは細かく書いたつもり。
コントローラとビューの作成
前の記事ではモデルのみを作りました。これによりデータベースはできました。次に、Webページ(アプリ)ということで、それを表示するページを表示したいわけですが、コントローラもビューもまだできていないので、作っていきましょう。
rails g controller page search
これで、pageというアプリとその中にsearchというビューが作成されました。
ちなみに、複数のビューを作りたい場合は、searchのあとに半角スペースを空けて複数のビューの名前を書けば自動で生成されます。
余談なのですが、個人的にはあるコントローラにビューだけを追加で作成するgenerateコマンドがあると、初学者は楽なのではとか思ったりします。
今回はpage#search(URLはhttp://localhost:3000/page/search )のビューに検索機能を付けていきます。
仕様の検討
今後カテゴリの検索に対応させたり、そもそも何も検索対象ワードが入力されていないときに全コンテンツを表示させたりしたいので、Railsでは普段あまり使わないかもしれないですが、URLパラメータを採用します。なお、これはRailsなどフレームワークでよくある?とかのつかないパラメータと同じ"params"という文で使えます。
なお今回、検索ワードを格納するパラメータは、Googleをまねてqとします。
つまりURLはhttp://localhost/page/search?q=テニス といった形になります。
また、本来データベースから内容を取り出すのはコントローラに書くべきだと思うのですが、個人的に面倒だったので(MVCとはなんだったのか)、とりあえず適当にビューに全部書きます。PHPをやり続けていたのでまだMVCに慣れていないです。
前記事からの引継ぎ内容
- モデルの名前はMysqlMatchTest
- nameにタイトル、descにその説明が書いてある。
- サンプルデータはWikipediaから持ってきた。
実装
すべてのデータを表示
まずはデータベースのすべてのデータを表示するように書いてみます。
<% @datas = MysqlMatchTest.all %>
<% @datas.each do |data| %>
<p><%= data.name %>: <%= data.desc %></p>
<% end %>
とてもすっきりしていますね。簡単に説明すると、
- @datasにデータベースのすべてのデータを入れる。
- each文でひとつずつオブジェクトを取り出していき、なくなり次第終了。
といった流れです。このうち@datasに投入するデータを検索で絞り込めばクリアです。
検索による絞り込み
P.S. 検索できないコードを上げていました。2019/12/26 13:26に修正しました。1行目がnilではなくemptyでした。
<% if params[:q] && !params[:q].empty? %>
<% query = "select * from `mysql_match_tests` where match (`desc`) against ('" + params[:q] + "')" %>
<% @datas = MysqlMatchTest.find_by_sql(query) %>
<% else %>
<% @datas = MysqlMatchTest.all %>
<% end %>
<%= form_tag('',:method => 'get') do %>
<%= text_field_tag :q,params[:q] %>
<%= submit_tag '検索', :name => nil %>
<% end %>
<% @datas.each do |data| %>
<p><%= data.name %>: <%= data.desc %></p>
<% end %>
簡単に説明します。
検索
上の6行が、検索のさいにデータベースから必要とされている情報のみを持ってくる部分です。
if文
1つ目の条件はそもそもパラメータqがあるかどうか、2つ目の条件はパラメータqに中身があるかどうかを確認します。
1つ目がないと、パラメータ自体がない場合、empty?文はtrueを返してしまうようで、queryという変数にパラメータqを代入する部分があるのでそこでエラーを吐いてしまいます。
2つ目がないと、何も入力せずに検索した場合に、match ... against ...文の仕様により該当なしで返してしまいます。
よって2つとも必要で、両方をandの論理演算子で結んでいます。
情報を取ってくる部分
実際に検索で絞り込みを行う、MATCH ... AGAINST ...
文はRailsのActiveRecordsではもちろんサポートされていないので、RailsからSQLクエリをそのまま実行する必要があります。
前の記事では、フルテキストインデックスを作るためにexecute文でSQLクエリを実行しましたが、今回はMySQLからデータを返してきて、データを格納する必要があるのでfind_by_sql
文を使います。
なお、検索ワードの反映が必要なので、先にqueryという変数に、実行したいSQLクエリを格納しました。
検索フォーム
検索フォームはその下の4行、form_tag
です。
今回は検索した際にURLパラメータを付けて、また同じページ(page#search)に戻ってくる必要があるので、form_tag文の第1変数は''としました(相対パス)。
テキストフィールドは2行目のtext_field_tag文によって生成されます。第1変数の:q
が、入力したデータをページ遷移時に格納するURLパラメータの名前。第2変数のparams[:q]
が、ページ遷移してきたときに最初にフォーム内に入れておきたい文章。今回は、遷移時に検索している単語を表示させました。
3行目は検索ボタン。後述の参考資料からそのまま引っ張ってきました。ちなみに、:name => nil
をつけないと、URLパラメータにcommit=検索
という変数が追加されてしまいます。
結果表示部分
each文を使っています。Ruby使い始めて最初に衝撃を受けたのはこの文法ですね。先に書いた「データを全部表示する」コードと同じです。
まとめ
自分がつまづいた部分は相当丁寧に書いたつもりなので、これを使ってRailsのモデルの扱い方やビューでの表示方法の理解にも役立ててもらえると、筆者としてもとてもうれしいです。
参考資料
Ruby on Railsのparamsメソッドの使い方を現役エンジニアが解説【初心者向け】
HTML5 : 入力フォームに最初からテキストを表示させる簡単な方法
railsで検索フォームを作ろう!!
Action View フォームヘルパー - Railsガイド v6.0
オブジェクトの存在確認の記法まとめ(if obj, if obj.nil?, if.obj.empty?, if obj.blank?, if obj.present?・・など)
Railsで変数の中身が空か確認する空チェックメソッド4種
論理演算子 - Let's プログラミング