I'm using Rails and I have two views in bookings (clients and trainers). I'm trying to route from index to client or trainers depending on who is the current_user.
If I'm a client, route index to trainers If I'm a trainer, route index to clients
class BookingsController < ApplicationController
def index
end
def clients
@bookings = Booking.where(client: current_user)
@clients = current_user.bookings.map(&:client)
end
def trainers
@trainers = current_user.bookings.map(&:user)
end
end
Rails.application.routes.draw do
resources :bookings do
collection do
get "/clients", to: "bookings#clients", as: "clients"
get "/trainers", to: "bookings#trainers", as: "trainers"
end
resources :shared_training_plans, except: [:new]
end
end
CodePudding user response:
You're doing nesting backwards. And the output of your routes should actually look more like:
GET /instructors/1/bookings # bookings belonging to instructor 1
GET /instructors/1/clients # clients that are associated with instructor 1
GET /students/3/bookings # bookings belonging to student 3
GET /students/3/instructors # instructors belonging to student 3
This describes RESTfully that the endpoint returns something belonging to the other resource. This should be handled by the index
action - ideally of a controller designated for that purpose as each controller should only be responsible for one resource.
You could define these routes as:
resources :instructors do
resources :bookings, module: :instructors
end
resources :clients do
resources :bookings, module: :clients
end
module Instructors
class ClientsController < ApplicationController
# GET /instructors/1/clients
def index
@instructor = Instructor.find(params[:instructor_id])
@clients = @instructor.clients
end
end
end
module Clients
class InstructorsController < ApplicationController
# GET /clients/1/clients
def index
@client = Client.find(params[:client_id])
@clients = @client.instrucors
end
end
end
You can implement this by reimagining the feature so that you link the "specific users URL" instead of a generic URL. You can see this for example on the dashboard here on Stackoverflow which uses https://stackoverflow.com/users/{id}/{username}
.
REST is in theory supposed to be stateless so each path should return the same resource no matter who is requesting it. But like all rules this could be broken and you can set up routes like:
GET /user/bookings # GET bookings for the currently signed in user
Which would return different results for the signed in user which is stateful. user
here would be consider a singular resource. This is mostly useful if you need to have a significantly different represention of the resource when viewing "your own" vs "others".
To actually be able to use the "current user" from the routes the authentication system must be implemented as Rack middleware (like Devise is) and not on the controller level as almost all the "reinventing the wheel" tutorials are. Routing is Rails is implemented as Rack middeware that is run before your controller is ever instanciated.
Devise has the authenticated
routing helper which lets you setup different routes for authenticated/unauthenticated users. For example if instructors are implemented as different warden scopes:
authenticated :instructor do
resource :bookings, controller: 'instructors/bookings', only: [:index]
end
authenticated :student do
resource :bookings, controller: 'students/bookings', only: [:index]
end
But you could also use a lambda if you only have one warden scope (the typical Devise configuration):
authenticate :user, ->{|u| u.instructor? } do
resource :bookings, controller: 'instructors/bookings', only: [:index]
end
authenticate :user, ->{|u| u.student? } do
resource :bookings, controller: 'students/bookings', only: [:index]
end
Both of these would route GET /bookings
to Instructors::BookingsController#index
and Students::BookingsController#index
depending on which "role" the user has.