Domain specific routing with different paths per domain for same model

165 Views Asked by At

I have a Consultant model, where i have multiple consultant types (lawyers, doctors, psychologists etc.) listed on different websites, all handled from the same rails project.

I would like to have the consultant type as a part of the url, but have a hard time figuring out how, since it is dynamic based on domain/consultant type.

I am hoping for a solution to do a standard link:

=link_to consultant.name, consultant

without any specific link-config, so I can re-use templates across multiple consultant-websites.

Urls should be like this:

a-domain.com/doctor/doctor-name

b-domain.com/lawyer/lawyer-name

What I've tried so far, and used in the domain-specific templates (i know it is an ugly solution):

routes.rb
get 'lawyer/:slug' => 'consultants#show', as: :lawyer_consultant
get 'doctor/:slug' => 'consultants#show', as: :doctor_consultant
_consultant.html.haml for a-domain.com
= link_to consultant.name, lawyer_consultant_path(consultant)

I know the easy solution would just be this;

get 'consultant/:slug' => 'consultants#show', as: :consultant

But i want the url to be specific. And the constraints: {host: a-domain.com} unfortunately does not allow for domain-specific routing, since only one as: :consultant can exist in routes.rb.

1

There are 1 best solutions below

0
max On

Routes don't actually have anything to do with your models. Your routes are the external REST API of your application while your models are an internal implementation detail.

I would just set the routes up as:

resources :doctors, 
          :lawyers,
          only: :index

This just describes RESTful resources in your application like any other. Your routes should neither know or care that a doctor is a kind of consultant - its just a thing that can be routed to. Nor should it care that you're using slugs, to the router :id is just some kind of identifier.

The only actual connection between routes and models are the polymorphic routing helpers which basically just look up the the name of routing helper method to call based on convention over configuration:

irb(main):005:0> app.polymorphic_path("doctors") # doctors_path
=> "/doctors"
irb(main):006:0> app.polymorphic_path("doctor", id: 1) # doctor_path(1)
=> "/doctors/1"
irb(main):006:0> app.polymorphic_path(Doctor.new) # doctors_path
=> "/doctors"
irb(main):006:0> app.polymorphic_path(Doctor.find(1)) # doctor_path(1)
=> "/doctors/1"

When you pass a model instance Rails 'calls model_name.route_key on the model instance and then will determine if its singular or plural by checking if the model has been persisted.

If you want the polymorphic routing helpers to "just work" one solution is using Single Table Inheritance:

class AddTypeToConsultants < ActiveRecord::Migration[6.1]
  def change
    add_column :consultants, :type, :string
  end
end
class Doctor < Consultant
end
class Lawyer < Consultant
end

When generating links you won't actually have to care about the type:

<%= link_to consultant.name, consultant %>

When you pass an instance of Doctor it will use doctor_path and when you pass an instance of Lawyer you get lawyer_path.

It also works for forms:

<%= form_with model: consultant do |f| %>
  # ...
<% end %>

You can also acheive the same thing with the decorator pattern if STI isn't your cup of tea.

class DoctorDecorator < SimpleDelegator
  def to_model
    self
  end

  def model_name
     ActiveModel::Name.new(Consultant, nil, "Doctor")
  end
end

doctor = DoctorDecorator.new(Consultant.find(1))
polymorphic_path(doctor) # /doctors/1