Search code examples
ruby-on-railsmodel-view-controllerrestful-architecture

Making the user just book one training not more


Im making an application where an user can book a hour of training. I want to give the app the restriction of when an user has already booked one hour of training, it cant book more trainings, at least he deletes it or the book expires.

My code is:

Bookings controller:

Class BookingsController < ApplicationController
  before_action :load_training,  only: [:create]

  def new
    @booking = Booking.new
    @training = Training.find(params[:training_id])
    @booking.training_id
  end

  def create
    @booking = @training.bookings.build(booking_params)
    @booking.user = current_user

    if @booking.save
      flash[:success] = "Book done"
      redirect_to trainings_path
    else
      render 'new'
    end
  end


  def index
    @bookings = Booking.where(training_id: params[:training_id])
  end


  def destroy
    @booking = Booking.find(params[:id])
    @booking.destroy
    flash[:success] = "Book deleted"
    redirect_to trainings_path
  end



private
  def booking_params
    params.require(:booking).permit(:user_id, :training_id)
  end

  def load_training
    @training = Training.find(params[:training_id])
  end

end

Booking model:

class Booking < ApplicationRecord
  belongs_to :user
  belongs_to :training
  default_scope -> { order(created_at: :desc) }
  validates :user_id, presence: true
  validates :training_id, presence: true



end

My routes.rb:

Rails.application.routes.draw do

  root 'static_pages#home'
  get    '/signup',               to: 'users#new'
  get    '/contact',              to: 'static_pages#contact'
  get    '/about',                to: 'static_pages#about'
  get    '/login',                to: 'sessions#new'
  post   '/login',                to: 'sessions#create'
  delete '/logout',               to: 'sessions#destroy'


  resources :account_activations, only: [:edit]
  resources :password_resets,     only: [:new, :create, :edit, :update]

  resources :trainings do
    resources :bookings
  end
  resources :users
end

Index of training view:

<h1>Hours</h1>

<ul class="trainings">
  <% @trainings.each do |training| %>
  <li>
    <%= link_to training.hour, training_path(training) %>
  </li>
  <% end %>
</ul>

Show of training view:

<div class="row">
    <section>
      <h1>
HOUR: <%= @training.hour %>
      </h1>
    </section>
    <section>
      <h1>
SLOTS: <%= @training.slots %>
      </h1>
    </section>
    <center>
    <%= render 'bookings/booking_form' if logged_in? %>
    <%= render 'bookings/index_bookings' if logged_in? %>
  </center>

Booking_form.html.erb view:

<% unless current_user?(@user) %>
      <% if current_user.is_booked(@training) %>
      <%= link_to "Delete book", training_booking_path(@training), method: "delete", data: { confirm: 'Are you certain you want to delete this?' }, class: "btn btn-primary" %>
      <% else %>
      <%= link_to "Book", new_training_booking_path(@training), class: "btn btn-primary" %>
    <% end %>
<% end %>

Im recieving the following error:

ActiveRecord::RecordNotFound in BookingsController#destroy

Couldn't find Booking with 'id'=1

Parameters:

{"_method"=>"delete", "authenticity_token"=>"0uUXRwZdbhaKl16QxDi1HCM4H8IwEvGuoFOoxmkHhowoAUgZnlWPybck9DEbCKHh42SqXs3vtc01IRTqbx05wA==", "training_id"=>"1", "id"=>"1"}

I would like to know why the method does not get the booking_id

When i try to see a training hour:

Started GET "/trainings/1" for 127.0.0.1 at 2017-03-11 01:03:23 -0400
Processing by TrainingsController#show as HTML
  Parameters: {"id"=>"1"}
  Training Load (0.1ms)  SELECT  "trainings".* FROM "trainings" WHERE "trainings"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Rendering trainings/show.html.erb within layouts/application
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  Rendered bookings/_booking_form.html.erb (2.3ms)
  Rendered trainings/show.html.erb within layouts/application (4.0ms)
Completed 500 Internal Server Error in 6ms (ActiveRecord: 0.2ms)



ActionView::Template::Error (No route matches {:action=>"show", :controller=>"bookings", :id=>nil, :training_id=>"1"} missing required keys: [:id]):
    2:       <% if current_user.not_booked(@training) %>
    3:       <%= link_to "Reservar", new_training_booking_path(@training), class: "btn btn-primary" %>
    4:       <% else %>
    5:       <%= link_to "Eliminar reserva", training_booking_path(@training, @booking), method: :delete,
    6:        data: { confirm: 'Are you certain you want to delete this?' }, class: "btn btn-primary" %>
    7:     <% end %>
    8: <% end %>

Training model:

class Training < ApplicationRecord
  has_many :users, through: :bookings
  has_many :bookings

  def can_book?
    bookings.count < cantidad
  end

  end

Training controller:

class TrainingsController < ApplicationController

 def show
    @training = Training.find(params[:id])
  end

  def index
    @trainings = Training.all
  end
end

Thanks


Solution

  • In this line:

    <%= link_to "Delete book", training_booking_path(@training), method: "delete", data: { confirm: 'Are you certain you want to delete this?' }, class: "btn btn-primary" %>
    

    I think the path training_booking_path(@training) should also contain the @booking instance variable because of the nested routes.

    So it should be training_booking_path(@training, @booking), method: :delete, etc.

    You'd have to have @booking available in your view where the form shows up

    I would use rake routes in your console to confirm the correct path and which resource ids need to be passed in

    EDIT:

    your trainings controller show action needs to have bookings available as an instance variable:

    def show
      @training = Training.find(params[:id])
      @bookings = @training.bookings
    end
    

    Then in your training show view where the form resides, you need to loop over the @bookings and include an individual delete link for each:

    <% @bookings.each do |booking| %>
      <%= link_to "Delete book", training_booking_path(booking.training, booking), method: :delete, data: { confirm: 'Are you certain you want to delete this?' }, class: "btn btn-primary" %>
    <% end %>