-
-
Notifications
You must be signed in to change notification settings - Fork 592
Root level slugs for multiple resources
Daniel Amireh edited this page Jul 6, 2020
·
3 revisions
Given two unrelated models, City and Flower. Instead of URLs like myapp/cities/amsterdam, we want the slugs on root level for both resources, so that the app can use myapp/amsterdam and myapp/tulips and render the correct view.
For a full walk-through (and some more detailed coding) see https://medium.com/@mauddev/root-level-slugs-for-multiple-controllers-with-friendlyid-1a4a7ba03ec1
- Create (or generate) a SlugsController with a :show action that uses the friendly_id_slugs table and its sluggable_type field to distinguish flowers and cities, and to render the view.
- Point root level slugs to the new SlugsController.
- Create a custom validator for uniqueness of slugs against both models.
# Step 1: SlugsController
class SlugsController < ApplicationController
def show
@slug = FriendlyId::Slug.find_by(slug: slug_params)
if @slug
render_view
else
raise ActiveRecord::RecordNotFound, notice: "This page does not exist"
end
end
private
def slug_params
@slug_params ||= params[:id]
end
def render_view
case @slug.sluggable_type
when "Flower"
@flower = Flower.friendly.find(slug_params)
render 'flowers/show'
when "City"
@city = City.friendly.find(slug_params)
render 'cities/show'
else raise ActiveRecord::RecordNotFound
end
end
end
# Step 2: routes.rb
# point all root level routes to slugs#show
resources :cities
resources :flowers
get "/:id", to: "slugs#show"
#Step 3: Create custom validator file in models/concerns
class SlugValidator < ActiveModel::EachValidator
ROOT_SLUG_MODELS = %w(Flower City)
def validate_each(record, attribute, value)
record.errors.add(attribute, "has already been taken") unless valid_slug?(value)
end
private
def valid_slug?(value)
ROOT_SLUG_MODELS.each do |model|
model = model.constantize
return false if model.friendly.exists_by_friendly_id?(value)
end
end
end
# and add database constraints