Rails routes with :name instead of :id url parameters

前端 未结 7 1934
渐次进展
渐次进展 2020-12-05 13:05

I have a controller named \'companies\' and rather than the urls for each company being denoted with an :id I\'d like to have the url use their :name such as: url/comp

相关标签:
7条回答
  • 2020-12-05 13:39

    There's actually no magic to implement this, you have to either build it yourself by correctly implementing to_param at your model (not recommended) or using one of the gems available for this like:

    • friendly_id
    • has_permalink

    I use friendly_id and it does the job nicely.

    0 讨论(0)
  • 2020-12-05 13:40

    The :id parameter is whatever comes after the slash when the URL is requested, so a name attribute needs to be extracted from this by checking the :id parameter for non-numerical values with regular expressions and the match? method in the controller. If a non-numerical value is present, the instance can be assigned by the name attribute using the find_by_name() method that rails generated for the model (assuming that the model has an attribute called name)

    That's how I figured out how to do it in my app with my Users resource. My users have a username attribute, and all I had to do was modify the UsersController to define my @user variable differently depending on the :id parameter:

    private
      # allow routing by name in addition to id
      def get_user
        if params[:id].match?(/\A\d+\Z/)
          # if passed a number, use :id
          @user = User.find(params[:id])
        else
          # if passed a name, use :username
          @user = User.find_by_username(params[:id])
        end
      end
    

    This gives me the option to use either id or username when I create a link to a particular user, or type it into the browser's address bar.

    Then the only other (optional) thing to do is to change all the links in the views so that they point to the URL with the name instead of the URL with the id.

    For example, within the link_to() method call in my navigation bar I changed

    ... user_path(current_user) ...
    

    to

    ... user_path(current_user.username) ...
    

    In your example, you might have a company view with the following link:

    <%= link_to @company.name, company_path(@company.name) %>
    

    Which, if the current company is Microsoft, would display "Microsoft" and link to "companies/Microsoft", even though the URL "companies/1" would still be valid and display the same thing as "companies/Microsoft"

    0 讨论(0)
  • 2020-12-05 13:48

    params

    The bottom line is you're looking at the wrong solution - the params hash keys are rather irrelevant, you need to be able to use the data contained inside them more effectively.

    Your routes will be constructed as:

    #config/routes.rb
    resources :controller #-> domain.com/controller/:id
    

    This means if you request this route: domain.com/controller/your_resource, the params[:id] hash value will be your_resource (doesn't matter if it's called params[:name] or params[:id])

    --

    friendly_id

    The reason you have several answers recommending friendly_id is because this overrides the find method of ActiveRecord, allowing you to use a slug in your query:

    #app/models/model.rb
    Class Model < ActiveRecord::Base
      extend FriendlyId
      friendly_id :name, use: [:slugged, :finders]
    end
    

    This allows you to do this:

    #app/controllers/your_controller.rb
    def show
        @model = Model.find params[:id] #-> this can be the "name" of your record, or "id"
    end
    
    0 讨论(0)
  • 2020-12-05 13:59

    Honestly, I would just overwrite the to_param in the Model. This will allow company_path helpers to work correctly.

    Note: I would create a separate slug column for complex name, but that's just me. This is the simple case.

    class Company < ActiveRecord::Base
      def to_param
        name
      end
    end
    

    Then change my routes param for readability.

    # The param option may only be in Rails 4+,
    # if so just use params[:id] in the controller
    resources :companies, param: :name
    

    Finally in my Controller I need to look it up the right way.

    class CompaniesController < ApplicationController
      def show
        # Rails 4.0+
        @company = Company.find_by(name: params[:name])
        # Rails < 4.0
        @company = Company.find_by_name(params[:name])
      end
    end
    
    0 讨论(0)
  • 2020-12-05 14:03

    Good answer with Rails 4.0+ :

    resources :companies, param: :name
    

    optionally you can use only: or except: list to specify routes

    and if you want to construct a URL, you can override ActiveRecord::Base#to_param of a related model:

    class Video < ApplicationRecord
      def to_param
        identifier
      end
    
      # or
      alias_method :to_param, :identifier
    end
    
    video = Video.find_by(identifier: "Roman-Holiday")
    edit_videos_path(video) # => "/videos/Roman-Holiday"
    
    0 讨论(0)
  • 2020-12-05 14:03

    I recommend using the friendly_id for this purpose. Please be noted that there are differences between friendly_id 4 and 5. In friendly_id 4, you can use like this

     @company = Company.find(params[:id])
    

    However, you won't be able to do that in friendly_id 5, you have to use:

     @company = Company.friendly.find(params[:id])
    

    In case that you don't want to use the params[:id] but params[:name], you have to override the route in routes.rb. For example

     get '/companies/:name', to: "companies#show"
    

    Hope these info would be helpful to you

    0 讨论(0)
提交回复
热议问题