質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.58%

Rails4.2でフォームにdate_selectやtime_selectを使うとパラメーターが取得できない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 5,334

score 11

前提・実現したいこと

Railsでデータの検索をする時にパラメーターを取得できず困っております。

リクナビやマイナビといった就職情報サイトの説明会を、複数サイトから説明会の開催時間も含めて検索できるサービスを作ろうと思っています。
検索の都度就職情報サイトにデータを取りに行くのではなく、事前に就職情報サイトをスクレイピングしてDBに保存してあるデータを検索します。
現在は企業情報や会社説明会といったデータはリクナビをスクレイピングしてある程度DBに保存してあります。

説明会のモデルBriefingSessionには

  • 開催地 : location(varchar)
  • 開催日 : bs_date(date)
  • 開始時間 : start_time(time)
  • 終了時間 : finish_time(time)

といったカラムがあります。()の中はデータ型です。
これらの日付や開催時間といったカラムから条件に合う説明会を検索をしようとしています。

検索フォームの実装Ruby on Railsで複数のモデルが絡む検索画面の作り方を参考にモデルに検索ロジックを作りました。

発生している問題・エラーメッセージ

入力フォームから開催日をdate_select、開始時間と終了時間をtime_selectで入力させたいのですが、入力してsubmitを押すと以下のエラーになります。

エラーメッセージ
undefined method `start_time(1i)=' for #<BriefingSessionSearchForm:0x007fba91fe9e28>

Parameters:

{"utf8"=>"✓",
 "briefing_session_search_form"=>{"location"=>"東京都",
 "bs_date"=>"2016-12-02",
 "start_time(1i)"=>"2016",
 "start_time(2i)"=>"12",
 "start_time(3i)"=>"1",
 "start_time(4i)"=>"10",
 "start_time(5i)"=>"00",
 "finish_time"=>""},
 "commit"=>"検索"}

(start_timeだけtime_selectにしてあります)

該当のソースコード

関係ありそうなファイルを載せます。
GitHubには全て上げてあります。

/app/controllers/briefing_sessions_controller.rb

class BriefingSessionsController < ApplicationController
  def index
    @search_form = BriefingSessionSearchForm.new
  end

  def search
    @search_form = BriefingSessionSearchForm.new(search_params)
    @bs_results = @search_form.matches
  end

  def show
    @bs = BriefingSession.find(params[:id])
  end

  private
  def search_params
    params.require(:briefing_session_search_form).permit(:location, :bs_date, :start_time, :finish_time)
  end
end

/app/views/briefing_sessions/index.html.erb

<h1>説明会検索</h1>

<%= form_for(@search_form, url: :search_briefing_sessions, html: {method: :get}) do |f| %>

  <%= f.label :location, "開催地" %>
  <%= f.text_field :location %><br>
  <%= f.label :bs_date, "開催日" %>
  <%= f.date_field :bs_date, use_month_numbers: true, discard_year: true %><br>
  <%= f.label :start_time, "開始時間" %>
  <%= f.time_select :start_time %><br>
  <%= f.label :finish_time, "終了時間" %>
  <%= f.time_field :finish_time %><br>

  <%= f.submit "検索" %>

<% end %>

/app/forms/briefing_session_search_form.rb

class BriefingSessionSearchForm
  include ActiveModel::Model
  attr_accessor :location, :bs_date, :start_time, :finish_time

  def matches
    results = BriefingSession
    results = results.where(location: location) if location.present?
    results = results.where(bs_date: bs_date) if bs_date.present?
    results = results.where(start_time: start_time) if start_time.present?
    results = results.where(finish_time: finish_time) if finish_time.present?

    results
  end
end

一応/app/models/briefing_session.rb

class BriefingSession < ActiveRecord::Base
  belongs_to :company
  has_many :briefing_session_urls, dependent: :destroy
  has_many :urls, through: :briefing_session_urls, dependent: :destroy

  validates :location, presence: true, length: { in: 2..12 }
  validates :bs_date, presence: true
  validates :start_time, presence: true
  validates :finish_time, presence: true
end

試したこと

モデルにbs_date(1i)start_time(1i)といったアクセサが無いからだと予想したので~(1i)といったアクセサを追加しようとしましたがエラーになります。
どうすればよいかが分かりません。

/app/forms/briefing_session_search_form.rb

REGISTRABLE_ATTRIBUTES = %i(
    location
    bs_date(1i) bs_date(2i) bs_date(3i)
    start_time(1i) start_time(2i) start_time(3i) start_time(4i) start_time(5i)
    finish_time(1i) finish_time(2i) finish_time(3i) finish_time(4i) finish_time(5i)
  )

attr_accessor REGISTRABLE_ATTRIBUTES
#=>[:location, :"bs_date(1i)", :"bs_date(2i)", :"bs_date(3i)", :"start_time(1i)", :"start_time(2i)", :"start_time(3i)", :"start_time(4i)", :"start_time(5i)", :start_time, :finish_time] is not a symbol nor a string

現状は説明会の開催地と開催日時でしか検索していませんが、開催する企業の業種や社員数、就職情報サイトの種類などでも検索できるようにするつもりです。
そのため検索条件が複雑になると思われるのですが、ransackでは複雑な検索は難しいと見かけたので、ransackは使わずにロジックを分けて実装したいです。

ですがそれはあくまで希望なので、実装することができたら他の方法でも構いません。
どなたか解決方法が分かる方いらっしゃったらどうかお願いいたします。

補足情報(言語/FW/ツール等のバージョンなど)

  • Mac OS X 10.11.6
  • PostgreSQL 9.5.5
  • Ruby 2.3.1
  • Rails 4.2.7.1
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

解決方法はいくつかあるかもしれませんが、自前で変換してあげる例を1つ。

class BriefingSessionSearchForm
  def initialize(params = {})
    if params.is_a?(ActionController::Parameters)
      [:start_time, :finish_time].each do |attribute|
        datetime_parts = (1..5).map { |i| params.delete("#{attribute}(#{i}i)") }
        params[attribute] = Time.zone.local(*datetime_parts) if datetime_parts.any?
      end
    end
    super
  end
end

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/12/02 02:06 編集

    ありがとうございます!
    例を参考にしてこんな感じにしたら検索できるようになりました。

    def initialize(params = {})
    if params.is_a?(ActionController::Parameters)
    date_parts = (1..3).map { |i| params.delete("bs_date(#{i}i)") }
    params[:bs_date] = date_parts.join("-") if date_parts.any?

    [:start_time, :finish_time].each do |attribute|
    time_parts = (1..5).map { |i| params.delete("#{attribute}(#{i}i)") }
    params[attribute] = Time.zone.local(*time_parts).to_s(:time) if time_parts.any?
    end
    end
    super
    end

    素早い回答本当にありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.58%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る