这是一个基本的常规控制器设置:
首先,这是路由快捷方式语法:
# config/routes.rb
...
resources :products
...
这是加载所有产品的控制器,index
仅加载一个产品show
。
# app/controllers/products_controller.rb
class ProductsController
def index
@products = Product.all
end
def show
@product = Product.find(params[:id])
end
end
路线文件中的那一行为我们提供了这些路线(以及其他路线):
/products
去products_controller#index
/products/:id
将products_controller#show
params[:id] 设置为给定的数字
如您所见,以最少的工作量获得漂亮 URL 的最简单方法是覆盖to_param
.
to_param
是 Rails 用来将模型转换为 url/request 中的表示的方法。
默认情况下,它是这样的:
class Product
def to_param
self.id
end
end
因此,当 Rails 对其结果执行 Product.find() 时to_param
,find()
将其转换为带有to_i
.
如果params[:id]
是"24"
,Product.find(params[:id])
则将“24”转换为整数 24。
但请考虑如何to_i
与其他示例一起使用:
"100".to_i #=> 100
"100-flux-capacitor-2000".to_i #=> 100
换句话说,如果我们的 Product 参数表示是"#{id}-the-product-name"
,Product.find(params[:id])
即使我们的 products#show URL 看起来像 , 仍然可以工作/products/56-ford-taurus
。
还有一件事:
"I love the Ford Taurus".parameterize #=> "i-love-the-ford-taurus"
解决方案 A
解决您的问题的最简单方法是像这样覆盖 to_param:
class Product
def to_param
"#{id}-#{name.parameterize}"
end
end
这种方法的好处是您更改了 Product 模型中的一个方法,而您的其余代码只是 Works。
缺点是您的网址看起来像:
/products/56-ford-taurus
代替:
/products/ford-taurus
解决方案 B
如果您想要后一个示例,您可以slug
在 Products 表中维护一个列。
例子:
Product
id: 58
name: Ford Taurus
slug: ford-taurus
然后你可以像这样覆盖 to_param:
class Product
def to_param
slug
end
end
现在, params[:id] 看起来像“ford-taurus”。
Product.find("ford-taurus")
由于 find() 需要一个整数,因此不再起作用,因此您可以替换Product.find(params[:id])
为Product.find_by_slug(params[:id])
.
你只需要确保每个产品都有一个独特的 slug。
您可以通过以下方式自动化 slug 创建过程:
class Product
before_create :make_slug
private
def make_slug
self.slug = name.parameterize
end
end
这样,您只需要在创建时设置产品名称,保存它,它的 slug 将成为 URL 友好的“无论产品名称是什么”。
- 优点:您的 URL 现在只包含产品名称,没有难看的产品 ID。
- 不利的一面:更改产品的 slug 会破坏 URL,这就是为什么我的 before_filter 示例仅在创建时设置 slug 而不是在您保存产品时设置。
现在 Rails 将生成您想要的 URL,您可以在如下视图中生成指向这些产品的链接:
# This would be in views/index.html.erb
<% @products.each do |product| %>
<%= link_to product.name, product %>
<% end %>
将为每个产品生成这种 html:
<a href="/products/56-ford-taurus">Ford Taurus</a> (if you used Solution A)
<a href="/products/ford-taurus">Ford Taurus</a> (if you used Solution B)