2

ice_cube gemを使用して定期的な予約を処理する予約システムを作成しています。has_manyは、繰り返しルールの出現ごとに 1 つBookingずつBookingItemあり、これらはBookingの after_save コールバックによって呼び出されるメソッドで作成されます。

BookingItemこれは、特定の時間にまだ予約がないことを確認することで二重予約を回避する検証を追加するまで、すべて正常に機能していBookingItemました。この検証により、予約フォームに表示したいエラーが発生しますが、現時点では、 のフォームに戻されていないBookingによってエラーが発生したため、 の保存が黙って妨げられています。BookingItemBooking

アプリ/モデル/booking.rb

class Booking < ActiveRecord::Base
  include IceCube

  has_many :booking_items, :dependent => :destroy

  after_save :recreate_booking_items!

  # snip

  private

  def recreate_booking_items!
    schedule.all_occurrences.each do |date|
      booking_items.create!(space: self.requested_space, 
                            booking_date: date.to_date,
                            start_time: Time.parse("#{date.to_date.to_default_s} #{self.start_time.strftime('%H:%M:00')}"),
                            end_time: Time.parse("#{date.to_date.to_default_s} #{self.end_time.strftime('%H:%M:00')}"))
    end
  end
end

アプリ/モデル/booking_item.rb

class BookingItem < ActiveRecord::Base
  belongs_to :booking

  validate :availability_of_space

  # snip

  private

    def availability_of_space
      unless space.available_between? DateTime.parse("#{booking_date}##{start_time}"), DateTime.parse("#{booking_date}##{end_time}")
        errors[:base] << "The selected space is not available between those times."
      end
    end
end

アプリ/ビュー/予約/_form.html.erb

<% if @booking.errors.any? %>
  <div id="error_explanation">
    <p><%= pluralize(@booking.errors.count, "error") %> prohibited this booking from being saved:</p>
    <ul>
      <% @booking.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>

<%= form_for(@booking, :html => { :class => "nice custom"}) do |f| %>
  ...
<% end %>
4

1 に答える 1

2

after_saveコールバックを使用してオブジェクトを作成する場合、オプションは多少制限されBookingItemます。

を使用する代わりに、それに対応するためにいくつかの調整をafter_save使用して行います。before_validation

1)コールバックでBookingItemオブジェクトを作成しますbefore_validation

before_validation :recreate_booking_items!

def recreate_booking_items!
  schedule.all_occurrences.each do |date|
    booking_items.build(......
  end
end

buildの代わりに使用していることに注意してくださいcreate!

オブジェクトを検証すると、コレクション内Bookingの新しいBookingItemオブジェクトも検証されます。booking_itemsエラーはすべてメインオブジェクトのエラーコレクションに含まれ、オブジェクトの保存に失敗するBookingため、通常どおりにビューに表示できます。Booking

ノート

1)オブジェクトは新しいレコードであり、関連付けに属しているためBookingItem、オブジェクトが検証されるときに自動的に検証されます。それらが永続化されている場合(つまり、すでにデータベースにある場合)、それらは自動的に検証されません。Bookinghas_many

2)before_validationコードによっては、オブジェクトのライフサイクルでコールバックを複数回呼び出すことができます。このような場合、BookingItemコールバックが呼び出されるたびにオブジェクトが作成されるため、重複が発生します。これを防ぐために、次の行を先頭に追加できますrecreate_booking_items!

booking_items.delete_all

もちろん、BookingItemデータベースにオブジェクトを永続化した場合は、これを実行したくない場合があります(以下を参照)。

3)このコードは、Bookingオブジェクトを作成するために明示的に設計されています。Bookingすでにオブジェクトが永続化されている既存のオブジェクトを編集している場合はBookingItem、必要な機能に応じて、特定の変更が必要になる場合があります。

アップデート:

以下のコメントで@Simonのフォローアップ質問に対処するため。

私はあなたがこれをしたいと思うかもしれない2つの方法を考えることができます:

1)検証をそのままにしておいてくださいBookingItem

Booking次に、次のようなカスタムバリデーターがあります。

validate :validate_booking_items

def validate_booking_items
  booking_items.each do |bi|
    if bi.invalid?
      errors[:base] << "Booking item #{bi.<some property>} is invalid for <some reason>"
    end
  end
end

Bookingこれにより、無効なメッセージごとに適切なカスタムメッセージが表示されますが、無効なメッセージを特定するために使用できる独自のエラーコレクションBookingItemも提供されます。次のように無効を参照できます。BookingItembooking_itemsbooking_items

@ booking.booking_items.select {| bi | bi.errors.present?}

次に、無効なbooking_itemsものをビューに表示する場合:

f.fields_for :booking_items, f.object.booking_items.select {|bi| bi.errors.present? } do |bi|
end

このアプローチの問題は、BookingItemいくつかの理由でaが無効になる可能性があり、それらすべての理由を基本Bookingエラーコレクションに追加しようとすると面倒になる可能性があることです。

したがって、別のアプローチ:

2)のカスタムバリデーターを忘れてくださいBooking。各オブジェクトhas_manyの検証チェックを実行するには、コレクションの非永続メンバーのRailsの自動検証に依存します。BookingItemこれにより、それぞれにエラーコレクションが提供されます。

次に、ビューで無効なものをループしてbooking_items、個々のエラーを表示できます。

<ul>
  <% @booking.booking_items.select {|bi| bi.errors.present? }.each do |bi| %>
    <li>
      Booking item <%= bi.name %> could not be saved because:
      <ul>
        <% bi.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
      </ul> 
    </li>
  <% end %>
</ul>

このアプローチを使用すると、Bookingオブジェクトエラーコレクションに一般的な「予約アイテムが無効です」というエラーが発生するため、表示されないように、これらを何らかの方法で無視することをお勧めします。

注:私はIceCubeに精通していませんが、BookingItemを介してフォームにオブジェクトを表示している場合、コールバックでオブジェクトnested_attributes_forを作成することと衝突する可能性があります。BookingItembefore_validation

于 2012-06-09T03:09:42.820 に答える