Я хотел бы, чтобы мой веб-сайт имел URL, бывшие похожие на это:
example.com/2010/02/my-first-post
У меня есть мой Post
модель с slug
поле ('my-first-post') и published_on
поле (от которого мы вычтем части года и месяца в URL).
Я хочу мой Post
модель, чтобы быть УСПОКОИТЕЛЬНЫМ, таким образом, вещи как url_for(@post)
работайте как, они должны, т.е.: это должно генерировать вышеупомянутый URL.
Существует ли способ сделать это? Я знаю, что необходимо переопределить to_param
и имейте map.resources :posts
с :requirements
набор опции, но я не могу заставить все это работать.
Мне сделали его почти, я - 90% там. Используя resource_hacks плагин я могу достигнуть этого:
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"}
и в представлении:
<%= link_to 'post', post_path(:slug => @post.slug, :year => '2010', :month => '02') %>
генерирует надлежащий example.com/2010/02/my-first-post
ссылка.
Я хотел бы, чтобы это работало также:
<%= link_to 'post', post_path(@post) %>
Но этому нужно переопределение to_param
метод в модели. Должно быть довольно легким, за исключением факта, этого to_param
должен возвратиться String
, нет Hash
поскольку я хотел бы его.
class Post < ActiveRecord::Base
def to_param
{:slug => 'my-first-post', :year => '2010', :month => '02'}
end
end
Результаты в can't convert Hash into String
ошибка.
Это, кажется, проигнорировано:
def to_param
'2010/02/my-first-post'
end
поскольку это заканчивается по ошибке: post_url failed to generate from {:action=>"show", :year=>#
Это все еще взлом, но работает следующее:
В 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
И следующее будет работать (как в Rails 2.3.x, так и в 3.0.0):
url_for(@post)
post_path(@post)
link_to @post.title, @post
etc.
Это ответ какой-то милой души на аналогичный мой вопрос, url_for настраиваемого ресурса RESTful (составной ключ; не только id) .
Это может быть полезно. Вы можете определить метод default_url_options
в вашем ApplicationController
, который получает хэш параметров, которые были переданы вспомогательному URL-адресу, и возвращает хэш дополнительных параметров, которые вы хотите использовать для этих URL-адресов.
Если сообщение задано как параметр для post_path
, оно будет назначено первому (неназначенному) параметру маршрута. Не тестировал, но может сработать:
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
У меня аналогичная ситуация, когда сообщение имеет параметр языка и параметр заголовка. Запись post_path (@post)
отправляет этот хэш методу default_url_options
:
{:language=>#<Post id: 1, ...>, :controller=>"posts", :action=>"show"}
ОБНОВЛЕНИЕ: существует проблема, что вы не можете переопределить параметры URL из этого метода. Параметры, переданные вспомогательному URL-адресу, имеют приоритет. Таким образом, вы можете сделать что-то вроде:
post_path(:slug => @post)
и:
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
Это сработает, если Post.to_param
вернет slug. Вам нужно только добавить год и месяц к хешу.
Райан Бейтс рассказал об этом в своем ролике экрана, «как добавить пользовательские маршруты, сделать некоторые параметры необязательными и добавить требования к другим параметрам». http://railscasts.com/episodes/ 70-custom-routes
Красивые URL для Rails 3.x и Rails 2.x без необходимости какого-либо внешнего плагина, но с небольшим хаком, к сожалению.
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
view
<%= link_to 'post', @post %>
Примечание, для Rails 3.x вы можете использовать это определение маршрута:
resources :posts
match '/:year/:month/:slug', :to => "posts#show", :as => :post, :year => /\d{4}/, :month => /\d{2}/, :slug => /[a-z0-9\-]+/
Есть ли значок за ответ на собственный вопрос? ;)
Btw: файл routing_test - хорошее место, чтобы посмотреть, что можно сделать с маршрутизацией Rails.
Обновление: Использование default_url_options
- это тупик. Выложенное решение работает только в том случае, если в контроллере определена переменная @post
. Если есть, например, @posts
переменная с Array of posts, то нам не повезло (потому что default_url_options
не имеет доступа к переменным вида, как p
в @posts.each do |p|
.
Так что эта проблема все еще остается открытой. Кто-нибудь поможет?