Search code examples
ruby-on-railsrubyruby-on-rails-4nested-resourcesfriendly-id

How to use friendly_id history with nested resources


I have User and Campaign models. I nest campaigns under users and URLs look like this:

http://localhost:3000/user-slug/campaign-slug

Routes:

resources :users, :path => '' do
  resources :campaigns, :path => ''
end

Campaign model:

class Campaign < ActiveRecord::Base
  ...
  extend FriendlyId
  friendly_id :title, use: :history
  ...
end

My User model doesn't use history.

Campaign controller (from friendly_id guide):

class CampaignsController < ApplicationController
  before_filter :find_campaign

  def show
    @campaign = Campaign.friendly.find(params[:id])
  end

  private

  def find_campaign
    @campaign = Campaign.friendly.find(params[:id])

    # If an old id or a numeric id was used to find the record, then
    # the request path will not match the post_path, and we should do
    # a 301 redirect that uses the current friendly id.
    if request.path != campaign_path(@campaign)
      return redirect_to @campaign, :status => :moved_permanently
    end
  end
end

When I visit an old slug to trigger redirection I get this error:

ActionController::UrlGenerationError in CampaignsController#show

No route matches {:action=>"show", :controller=>"campaigns", :format=>nil, :id=>nil, :user_id=>#<bunch of other stuff in here>} missing required keys: [:id]

Not sure how I should tweak the redirect method to make it work.


Solution

  • Managed to get it to work with this:

      def find_campaign
        @campaign = Campaign.friendly.find(params[:id])
        @user = @campaign.user
    
        # If an old id or a numeric id was used to find the record, then
        # the request path will not match the campaign_path, and we should do
        # a 301 redirect that uses the current friendly id.
        request_slug = params[:id]
        if request_slug != @campaign.slug
          return redirect_to user_campaign_path(@user, @campaign), :status => :moved_permanently
        end
      end
    

    Instead of comparing the request path I compared the request slug which doesn't have the part before the slash. And I needed to redirect to the correct route user_campaign_path.