What I have at the moment is pretty standard set of code, where all child objects can be list under only their parent objects.
customer.rb
class Customer < ApplicationRecord
has_many :bookings, dependent: :delete_all
end
booking.rb
class Booking < ApplicationRecord
belongs_to :customer
has_many_attached :images
end
routes.rb
Rails.application.routes.draw do
resources :customers do
resources :bookings
end
end
bookings_controller.rb
This has been automatically generated. I only removed comments and json related lines.
class BookingsController < ApplicationController
before_action :set_customer
before_action :set_booking, only: %i[show edit update destroy]
def index
@bookings = Booking.all.with_attached_images
end
def show; end
def new
@booking = @customer.bookings.build
end
def edit; end
def create
@booking = @customer.bookings.build(booking_params)
respond_to do |format|
if @booking.save
format.html { redirect_to @customer, notice: 'Booking was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if @booking.update(booking_params)
format.html { redirect_to [@customer, @booking], notice: 'Booking was successfully updated.' }
else
format.html { render :edit }
end
end
end
def destroy
@booking.destroy
respond_to do |format|
format.html { redirect_to customer_bookings_url, notice: 'Booking was successfully destroyed.' }
end
end
private
def set_customer
@customer = Customer.find(params[:customer_id])
end
def set_booking
@booking = @customer.bookings.find(params[:id])
end
def booking_params
params.require(:booking).permit(:name, :category, :rooms, :wifi, :phone, :address, :description, :available, :check_in, :check_out, :customer_id, images: [])
end
end
I want to list all child objects for all parent objects.
I guess, I will have to modify routes as follows
routes.rb
Rails.application.routes.draw do
root 'customers#index'
resources :customers do
resources :bookings
end
resources :bookings
end
I will also need to modify bookings_controller.rb
By commenting out the line before_action :set_customer
, otherwise I will get an error like Couldn't find Customer without an ID
And I will have to put @customer = Customer.find(params[:customer_id])
for all methods except index
. Which means I won't be following DRY concept...
Any other better approach to solve this?
Your approach is the best already in my opinion, just need to utilize Rails helpers correctly to keep your code DRY.
By commenting out the line before_action :set_customer, otherwise I will get an error like Couldn't find Customer without an ID
And I will have to put @customer = Customer.find(params[:customer_id]) for all methods except index. Which means I won't be following DRY concept...
NO you don't have to.
If the index
action of customers/bookings_controller
is not used anywhere else then just remove that action from the controller file and specify the same in the route file as:
resources :customers do
resources :bookings, except: :index
end
If the index
action is still being used in other places then Rails callbacks can be declared with except
option as below to specify that the set_customer
will be called for all actions except the index
.
before_action :set_customer, except: :index
More about Rails Controller Callback options here
Other points that you may want to check:
dependent: :delete_all
. With this, there will be orphan active_storage_attachments
records in your db when you delete a customer
. Because it triggers the callback that deletes only the associated bookings
when leave the attached images
of those bookings untouched. Referenceresources :bookings (last line of your route file)
. If you only have the index
action in the controller, you should declare the same here also as resources :bookings, only: :index