问题
I have a Company and a User model, both with a slug via friendly_id. Slugs are ensured to be unique across both models.
I'd like to have URLs:
http://www.example.com/any_company_name
http://www.example.com/any_user_name
e.g. both /apple
and /tim
I'm not sure how to achieve this in Rails.
I have tried various permutations of:
routes.rb:
resources :users, path: ''
resources :companies, path: ''
get '*search', to: 'my_controller#redirect'
and
my_controller#redirect:
@company = Company.friendly.find(params[:search])
redirect_to @company if @company
@user = User.friendly.find(params[:search])
redirect_to @user if @user
However I can't get it to work. I can get /apple
to redirect to /companies/apple
and /tim
to redirect to /users/tim
(by removing the path: ''
option) but this is not what I want to achieve.
回答1:
Instead redirect with urls, you can redirect with controller#action by using url_for.
For example:
my_controller#redirect:
@company = Company.friendly.find(params[:search])
redirect_to url_for(action: 'show', controller: :companies , status: :success, company_id:@company.id)
@user = User.friendly.find(params[:search])
redirect_to url_for(action: 'show', controller: :users, status: :success,user_id:@user.id)
回答2:
I had a similar problem and was able to solve it by creating a PublicSlug model with a slug attribute and a polymorphic association to a "Sluggable" class. I also used a Sluggable concern that I included in models I would like to query.
The PublicSlug model
class PublicSlug < ActiveRecord::Base
extend FriendlyId
friendly_id :sluggable_name, use: :slugged
belongs_to :sluggable, polymorphic: true
private
# Generate the slug based on the title of the Sluggable class
def sluggable_name
sluggable.name
end
end
The Sluggable concern
module Sluggable
extend ActiveSupport::Concern
included do
has_one :public_slug, dependent: :destroy, as: :sluggable
delegate :slug, to: :public_slug
end
end
Company and User models.
class User < ActiveRecord::Base
include Sluggable
end
class Company < ActiveRecord::Base
include Sluggable
end
I can now query both models using
Sluggable.friendly.find(slug).sluggable
The redirect could be handled in your controller as follows:
def redirect
@sluggable = Sluggable.friendly.find(params[:search]).sluggable
redirect_to @sluggable
end
回答3:
Building off of @Essn's answer...
Still use a PublicSlug model:
# app/models/public_slug.rb
class PublicSlug < ActiveRecord::Base
extend FriendlyId
friendly_id :sluggable_name, use: :slugged
belongs_to :sluggable, polymorphic: true
validates :slug, presence: true, uniqueness: { case_sensitive: false }
private
# Generate the slug based on the title of the Sluggable class
def sluggable_name
sluggable.name
end
end
And a Sluggable concern:
# app/models/concerns/sluggable.rb
module Sluggable
extend ActiveSupport::Concern
included do
before_validation :create_public_slug
has_one :public_slug, dependent: :destroy, as: :sluggable
delegate :slug, to: :public_slug
private
def create_public_slug
self.public_slug = PublicSlug.new unless public_slug.present?
end
end
end
Include that concern in all models you want to lookup:
# app/models/user.rb
class User < ActiveRecord::Base
include Sluggable
...
end
Create a migration:
# db/migrate/...create_public_slugs.rb
class CreatePublicSlugs < ActiveRecord::Migration[5.0]
def change
create_table :public_slugs do |t|
t.references :sluggable, polymorphic: true
t.string :slug
end
end
end
Then you can lookup the model via:
# app/controllers/home_controller.rb
class HomeController < ApplicationController
# /:slug
def show
@sluggable = PublicSlug.friendly.find(params[:id]).sluggable
render @sluggable
end
end
来源:https://stackoverflow.com/questions/27371487/match-multiple-models-in-single-rails-route-with-friendly-id