Skip to content

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

Steps

  1. 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.
  2. Point root level slugs to the new SlugsController.
  3. 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
Clone this wiki locally