Ben Smith
Ben Smith

Reputation: 829

Count the amount of records are associated to another record

In my Ruby on Rails application I am creating a cinema system, and on the bookings/new page I am allowing the user to choose the amount of seats they require through a drop down menu. But what I want to do is display the number of seats that are currently free in the screen, for example if a screen has 50 seats and 7 have been booked I want the system to display: "There are 43 seats available." I know I will need a method for this but am unsure about how I would implement it and how I would show this message.

It is worth noting that a seat would only be booked for one showing, so it would be free for others, which means that the method would have to be able to count the amount of seats available for that showing.

Can someone please help.

bookings/form.html.erb:

<%= form_for @booking do |f| %>
    <%= f.hidden_field :user_id %>
    <%= f.hidden_field :showing_id %>

    <%= image_tag "thor_hammer.jpg",:size => "900x250" %>
    <h1>NEW BOOKING:</h1>
    <tr>
        <td width="350px">
            <br><%= f.label :seats_quantity, 'Please Select The Amount of Seats Required:' %>
        </td>
        <td width="300px">
            <br><%= f.select :seats_quantity, '1'..'10' %><br>
        </td>
        <td width="300px">
            <div class="actions">
                 <br><%= f.submit 'Book Showing' %>
            </div>
            <br><%= render "/error_messages", :message_header => "Cannot save: ", :target => @booking %> 
        </td>
    </tr>
<% end %>

Screen.rb:

class Screen < ActiveRecord::Base
   has_many :seats
   has_many :showings
   def screens_info
       "#{name}"
   end
end

Seat.rb:

class Seat < ActiveRecord::Base
    belongs_to :screen
end

Booking.rb:

class Booking < ActiveRecord::Base
    belongs_to :user
    belongs_to :showing
end

Showing.rb:

class Showing < ActiveRecord::Base
   belongs_to :film
   has_many :bookings
   belongs_to :screen
end

Schema:

create_table "bookings", force: :cascade do |t|
    t.integer  "user_id"
    t.integer  "showing_id"
    t.integer  "seats_quantity"
end

create_table "screens", force: :cascade do |t|
   t.string   "name"
end

create_table "showings", force: :cascade do |t|
   t.date     "show_date"
   t.time     "show_time"
   t.integer  "film_id"
   t.integer  "screen_id"
end

create_table "seats", force: :cascade do |t|
   t.string   "row_letter"
   t.integer  "row_number"
   t.integer  "screen_id"
end

It is worth noting that whilst the seats table contains the attributes row_letter and row_number a user IS NOT booking a specific seat, just the quantity of seats they require.

Upvotes: 2

Views: 242

Answers (3)

smathy
smathy

Reputation: 27971

In your Screen class add:

has_many :bookings, through: :showings

And then your code becomes something like:

def remaining_seats
  seats.count - bookings.sum(:seats_quantity)  # <-- edited when I realized there was a quantity in a booking
end

def screens_info
  "#{name} (#{remaining_seats}/#{seats.count} remaining)"
end

Upvotes: 1

evanbikes
evanbikes

Reputation: 4171

EDIT

The manual way of doing this would be like so:

class Showing < ActiveRecord::Base
  def booked_seats
    bookings.pluck(:seats_quantity).sum
  end

  def available_seats
    seats.count - booked_seats
  end
end

OLD

This looks like a good use of Rails' counter_cache.

class Booking < ActiveRecord::Base
  belongs_to :showing, counter_cache: true
end

This will store the count in a column on the Showing model (which you have to add). Then when you do @showing.bookings.size (not count), it will refer to that column.

"With this declaration, Rails will keep the cache value up to date, and then return that value in response to the size method." http://guides.rubyonrails.org/association_basics.html

Upvotes: 0

hbejgel
hbejgel

Reputation: 4847

You need to figure out two values: the total seats for a specific showing and how many of those seats are already booked. Supposing you have two variables called scr_id and shw_id where the first represents the Screen Id and the second the Showing Id.

Total seats:

total_seats = Seat.where(screen_id: scr_id).count

Total bookings:

total_bookings = Booking.where(showing_id: shw_id).count

And then you only need to compute the differente between both.

available_seats = total_seats - total_bookings

EDIT: SPECIFIC IMPLEMENTATION

It should be implemented as a method in the screen model:

def available_seatings(shw_id)
    total_seats = Seat.where(screen_id: this.id).count
    total_bookings = Booking.where(showing_id: shw_id).count
    return total_seats - total_bookings
end

Then in the controller

@available_seatings = screen.available_seatings(shw_id)

You can then use the @available_seatings variable in the view

Upvotes: 0

Related Questions