Altere o nome do parâmetro: id em Recursos de roteamento para Rails

107

Eu olhei em volta para saber como alterar o slot de parâmetros dinâmicos e encontrei este post que faz a coisa exata. A postagem é https://thoughtbot.com/blog/rails-patch-change-the-name-of-the-id-parameter-in

Basicamente, o que ele faz é, se seguir as rotas:

map.resources :clients, :key => :client_name do |client|
  client.resources :sites, :key => :name do |site|
    site.resources :articles, :key => :title
  end
end

Essas rotas criam os seguintes caminhos:

/clients/:client_name
/clients/:client_name/sites/:name
/clients/:client_name/sites/:site_name/articles/:title

Uma solução é substituir o def to_parammétodo no modelo, mas quero isso sem tocar no próprio modelo.

Mas como é para Rails 2.x, como posso conseguir o mesmo para Rails 3?

Atualizar

Este aplicativo está usando o Mongoid. Não AR. Assim, a gema amigável não pode ser usada afaik.

Autodidata
fonte

Respostas:

192

Trilhos 4 e 5

No Rails 4, :paramfoi adicionada a opção, que parece fazer exatamente o que você está procurando. Você pode dar uma olhada no código do Rails 3 em comparação com o código do Rails 4 .

Detalhes

Você pode implementar isso facilmente em seu routes.rbarquivo:

# config/routes.rb

resources :posts, param: :slug

# app/controllers/posts_controller.rb

# ...
@post = Post.find_by(slug: params[:slug])
# ...

A partir do lançamento do Rails 4, esta funcionalidade está documentada nos Guias do Rails .

Rails 3

Infelizmente, no Rails 3, a :keyopção para resourcesfoi removida, então você não pode mais alterar facilmente o nome das rotas criadas desta forma apenas passando uma opção extra.

Detalhes

Suponho que você já tenha feito o aplicativo funcionar da maneira que deseja no ano passado, mas irei abordar uma maneira de obter o efeito que você descreve no Rails 3 em routes.rb. Envolverá apenas um pouco mais de trabalho do que o to_parammétodo. Você ainda pode definir parâmetros personalizados em rotas definidas usando scopee match(ou é primos get, put, post, e delete). Você simplesmente escreve o nome do parâmetro que deseja no matcher:

get 'clients/:client_name', :to => 'clients#show', :as => client

scope 'clients/:client_name' do
  get 'sites/:name', :to => 'sites#show', :as => site
end

Você teria que adicionar manualmente todas as rotas que resourcescria automaticamente para você, mas alcançaria o que você está procurando. Você também pode usar efetivamente a :controlleropção com blocos scopeadicionais scopepara eliminar parte da repetição.


EDIT (8 de maio de 2014): Torne mais óbvio que a resposta contém informações para Rails 3 e 4. Atualize os links para o código para ir para números de linha exatos e commits para que eles funcionem por um longo período de tempo.

EDITAR (16 de novembro de 2014): Rails 4 deve estar no topo agora e incluir informações relevantes, já que é a versão atual do Rails há algum tempo.

EDIT (9 de agosto de 2016): Reflita que a solução ainda funciona no Rails 5 e atualize links desatualizados.

Joshepworth
fonte
1
Então, basicamente, Rails3 criou a opção de recursos no roteador para ser tranquila, mas precisa ser substituída para ter nomes de variáveis ​​personalizados ... Parece coxo!
Augustin Riedinger
1
Aqui está um backport da paramopção para trilhos 3: gist.github.com/sj26/44ef47fe8b98b46ee32d
sj26
Isso acaba quebrando os ajudantes de url. Eles acabam gerando uma rota normal baseada em id se eu passar um objeto ou reclamando sobre a falta de uma chave [: slug] se eu passar o slug. Alguma idéia de como consertar?
RonLugge
9
Para sua informação, se você usar recursos aninhados, o parâmetro para o recurso pai será post_slug, o que pode ser confuso.
ghayes
4
Isso é muito bom, mas o que posso fazer se não quiser o nome do parâmetro do id pai como post_post_id para o recurso aninhado?
Asnad Atta
45

no Rails 4, passe a opção param para alterar os: id params. Por exemplo, resources :photos, param: :photo_nameirá gerar / photos /: photo_name

bobzsj87
fonte
1

Se entendi bem, o que você quer é ter o em client_namevez de idna sua url, certo?

Você pode fazer isso substituindo o to_parammétodo em seu modelo. Você pode obter mais informações aqui .

Mohit Jain
fonte
1
Como já mencionei, tenho a restrição de não alterar o modelo. O exemplo acima mencionado não é compatível ou obsoleto no Rails 3?
Autodidata
1

Existe uma joia para isso, assim como existe uma joia para tudo;)

Tenho usado o FriendlyId para esse tipo de comportamento no Rails 3.

Será necessário que você adicione algum código ao seu modelo, como este:

class Client < ActiveRecord::Base
  has_friendly_id :name
end

... e se seus clientes não têm nomes compatíveis com URI, você pode querer usar um slug para isso, o que você pode usar has_friendly_id :name, :use_slug => true. Ao usar slugs, você obviamente precisará persisti-los no banco de dados também.

E como já mencionado, você ainda pode usar o to_paramtruque com o rails 3, conforme documentado aqui . Acho o FriendlyId um pouco mais versátil.

geada
fonte
Gosto de id amigável e tenho usado em muitos projetos. Na verdade, meu aplicativo está usando o Mongoid. E friendly_id não suporta afaik. E como já mencionei, minha restrição não é mudar o modelo.
Autodidata
1
Se você estiver usando o mongoid, existe o mongoid-slug para obter essa funcionalidade. Na verdade to_param, ele usa , mas também faz outras coisas inteligentes em segundo plano. Encontrei o link aqui: stackoverflow.com/questions/4744446/…
Frost
Eu gostei daquela joia. Existe outro slugóide semelhante . Mas, como já disse, tenho a restrição de não modificar o modelo. Isso to_paramquebra minha parte do front-end do aplicativo. Na verdade, estou procurando desesperadamente pela abordagem de roteamento. Devo alterar minha API, mas sem quebrar a parte de modelo e front-end do aplicativo. Por que não posso apenas ter a abordagem de apenas modificar as rotas apenas como eu disse e o post de exemplo para uma versão mais antiga do rails acima ???
Autodidata de
Você tentou reescrever seus controladores para que usem suas chaves de rota personalizadas? Por exemplo, @client = Client.find(:name => param[:client_name]em ClientsController#show? Isso pode funcionar.
Frost
Client.where(:name => param[:client_name]).first, isso é.
Frost
0

No Rails 3 você pode renomear as chaves de id usando uma combinação de namespaces e escopos como este (não muito bom):

namespace :clients do
  scope "/:client_name" do
    namespace :sites do
      scope "/:name" do
         post "/:title" => "articles#create"
         ...
      end
    end
  end
end
Dominik Goltermann
fonte