在许多项目中,我们可能都会遇到需要为数据生成 slug 的场景,这些场景类似于:
基于商品名称生成 slug
基于文章标题生成 slug
至于为什么需要生成 slug,而不是使用比如 Rails 中默认自增的主键也就是数据的 id,原因其实很简单:
使用自增 id 容易暴露数据,比如通过订单 id 可能导致遍历所有订单,不信,你看这里就有个例子
增加 URL 友好性,/products/18376 这样的链接肯定没有比 /products/apple-watch-gold 这样的链接更招人喜欢
friendly_id
friendly_id 是用来生成 slug 的 ruby gem,假设我们有一个产品模型 Product
,使用 friendly_id 为商品名称(name
)生成 slug 的示例代码如下:
class Product < ActiveRecord::Base
friendly_id :name, use: :slugged
end
上面的代码理论上来说已经完成我们所需要的工作了,但是如果 title 包含中文的话,生成的 slug 就有点类似 30f175f4-1e56-4e3a-823d-a7c1a5d32b29
这样的乱码了,实际上这个 slug 对应的原来的 title 是 测试产品
。这样的 slug 虽然避免了自增 id 的弊端,但是却丧失了友好性。如果 slug 能够基于汉语拼音生成,岂不更好?
自己控制 slug 生成方式
阅读 friendly_id 的源码可以找到以下代码:
module FriendlyId
module Slugged
# ...
def normalize_friendly_id(value)
value.to_s.parameterize
end
# ...
end
end
这段代码便是 friendly_id 基于输入生成 slug 的核心代码,使用 ActiveSupport 扩展后的 String
类的 parameterize
方法,此方法会将除了英文字母、数字、短横线以及下划线之外的字符转换为 -
,所以不适用于中文的情况,我们需要重写该方法,以满足我们的需求。
中文拼音利器——chinese_pinyin
中文生成中文拼音的工具,我选择了黄志敏先生写的 chinese_pinyin
这个 gem,推荐理由就是简单够用。
以下是单独使用这个 gem 时的示例:
2.2.0 :009 > Pinyin.t("中国人")
=> "zhong guo ren"
2.2.0 :010 > Pinyin.t("Hello, 李雷")
=> "Hello li lei"
组装!!!
根据 friendly_id 的注释,如果你只需要为单独一个 model 定制 slug 的生成逻辑,那么建议你只在相关的 model 中定义同名方法即可。但是由于我是需要为多个 model 定制中文的 slug 生成逻辑,所以我选择了直接重定义 FriendlyId::Slugged
模块中的这个方法:
# config/initializers/friendly_id/slugged.rb
module FriendlyId
module Slugged
# 重定义 friendly_id 方法,实现 slug 从中文到拼音,非中文不受影响
def normalize_friendly_id(value)
Pinyin.t(value.to_s).parameterize
end
end
end
这样的定义方式使得新的 normalize_friendly_id
方法对所有依赖 friendly_id 的代码都生效。
最后通过新的方法为我们的产品生成新的 slug,现在“测试产品”得到的 slug 变为 ce-shi-chan-pin
了:
product.update(slug: nil) # 显式清空 slug, friendly_id 在 save 时会自动重新生成 slug
最后产品的链接已变为 /products/ce-shi-chan-pin
,比起 /products/30f175f4-1e56-4e3a-823d-a7c1a5d32b29
,可真是叫人心旷神怡。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。