Question

Je voudrais que mon site web pour avoir des URL qui ressemble à ceci:

example.com/2010/02/my-first-post

J'ai mon modèle Post avec champ slug ( 'my-first post') et sur le terrain de published_on (dont nous déduirons les pièces année et le mois dans l'url).

Je veux que mon modèle Post être RESTful, donc les choses comme le travail de url_for(@post) comme ils le devraient, à savoir: il devrait générer l'URL ci-dessus

.

Est-il possible de le faire? Je sais que vous devez remplacer to_param et ont map.resources :posts avec option :requirements ensemble, mais je ne peux pas tout travailler.


Je l'ai presque fait, je suis 90% là. En utilisant resource_hacks plug-in je peux réaliser ceci:

map.resources :posts, :member_path => '/:year/:month/:slug',
  :member_path_requirements => {:year => /[\d]{4}/, :month => /[\d]{2}/, :slug => /[a-z0-9\-]+/}

rake routes
(...)
post GET    /:year/:month/:slug(.:format)      {:controller=>"posts", :action=>"show"}

et dans la vue:

<%= link_to 'post', post_path(:slug => @post.slug, :year => '2010', :month => '02') %>

génère lien correspondant de example.com/2010/02/my-first-post.

Je voudrais que cela fonctionne aussi:

<%= link_to 'post', post_path(@post) %>

Mais il faut redéfinissant la méthode to_param dans le modèle. Devrait être assez facile, à l'exception du fait que to_param doit retourner String, pas Hash comme je le voudrais.

class Post < ActiveRecord::Base
  def to_param
   {:slug => 'my-first-post', :year => '2010', :month => '02'}
  end
end

Résultats en erreur can't convert Hash into String.

Cela semble être ignoré:

def to_param
  '2010/02/my-first-post'
end

car il se traduit par erreur: post_url failed to generate from {:action=>"show", :year=>#<Post id: 1, title: (...) (il attribue à tort objet @post à la touche: l'année). Je suis un peu désemparés sur la façon de le pirater.

Était-ce utile?

La solution 2

Il est encore un hack, mais les travaux suivants:

En application_controller.rb:

def url_for(options = {})
  if options[:year].class.to_s == 'Post'
    post = options[:year]
    options[:year] = post.year
    options[:month] = post.month
    options[:slug] = post.slug
  end
  super(options)
end

Et ce qui suit fonctionnera (aussi bien dans Rails 2.3.x et 3.0.0):

url_for(@post)
post_path(@post)
link_to @post.title, @post
etc.

Ceci est la réponse de quelques belle âme pour une question similaire à moi, url_for d'une ressource personnalisée RESTful (clé composite, pas seulement id) .

Autres conseils

jolies URL 3.x rails et 2.x sans la nécessité d'un plug-in externe, mais avec un petit hack, malheureusement.

routes.rb

map.resources :posts, :except => [:show]
map.post '/:year/:month/:slug', :controller => :posts, :action => :show, :year => /\d{4}/, :month => /\d{2}/, :slug => /[a-z0-9\-]+/

application_controller.rb

def default_url_options(options = {})
  # resource hack so that url_for(@post) works like it should
  if options[:controller] == 'posts' && options[:action] == 'show'
    options[:year] = @post.year
    options[:month] = @post.month
  end
  options
end

post.rb

def to_param # optional
  slug
end

def year
  published_on.year
end

def month
  published_on.strftime('%m')
end

vue

<%= link_to 'post', @post %>

Remarque, pour 3.x Rails vous pouvez utiliser cette définition de l'itinéraire:

resources :posts
match '/:year/:month/:slug', :to => "posts#show", :as => :post, :year => /\d{4}/, :month => /\d{2}/, :slug => /[a-z0-9\-]+/

Y at-il un badge pour répondre à votre question? ;)

BTW: le fichier routing_test est un bon endroit pour voir ce que vous pouvez faire avec routage Rails.

Mise à jour: Utilisation default_url_options est une impasse . La solution affichée ne fonctionne que quand il est variable @post définie dans le contrôleur. S'il y a, par exemple, @posts variable Tableau de messages, nous sommes hors de la chance (becase default_url_options n'a pas accès à voir les variables, comme dans p @posts.each do |p|.

Donc cela reste un problème ouvert. Quelqu'un d'aide?

Ryan Bates a parlé dans son écran CAST « comment ajouter des itinéraires personnalisés, faire quelques paramètres optionnels et ajouter des exigences pour les autres paramètres. » http://railscasts.com/episodes/70-custom-routes

Cela peut être utile. Vous pouvez définir une méthode default_url_options dans votre ApplicationController qui reçoit un Hash les options qui ont été transmises à l'aide d'URL et retourne une table de hachage d'options supplémentaires que vous souhaitez utiliser pour les urls.

Si un poste est donnée comme paramètre à post_path, il sera attribué au premier paramètre (unnassigned) de la route. Je n'ai pas testé, mais il pourrait fonctionner:

def default_url_options(options = {})
  if options[:controller] == "posts" && options[:year].is_a?Post
    post = options[:year]
    {
      :year  => post.created_at.year,
      :month => post.created_at.month,
      :slug  => post.slug
    }
  else
    {}
  end
end

Je suis dans la même situation, où un poste a un paramètre de langue et paramètre slug. post_path(@post) écriture envoie ce hachage à la méthode default_url_options:

{:language=>#<Post id: 1, ...>, :controller=>"posts", :action=>"show"}

Mise à jour: Il y a un problème que vous ne pouvez pas remplacer les paramètres d'URL de cette méthode. Les paramètres transmis à l'aide d'URL ont priorité. Ainsi, vous pouvez faire quelque chose comme:

post_path(:slug => @post)

et

def default_url_options(options = {})
  if options[:controller] == "posts" && options[:slug].is_a?Post
    {
      :year  => options[:slug].created_at.year,
      :month => options[:slug].created_at.month
    }
  else
    {}
  end
end

Ceci fonctionnerait si Post.to_param retourné la limace. Vous ne devez ajouter l'année et le mois au hachage.

Vous pouvez simplement vous épargner le stress et utiliser friendly_id. Son impressionnant, fait le travail et vous pourriez regarder un screencast par Ryan Bates pour commencer.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top