通过 hashid 的 ActiveRecord 查询
ActiveRecord query by hashid
我们使用 https://github.com/peterhellberg/hashids.rb 来混淆 API:
中的数据库 ID
HASHID = Hashids.new("this is my salt")
product_id = 12345
hash = HASHID.encode(product_id)
=> "NkK9"
解码哈希值时,我们必须做这样的事情:
Product.find_by(id: HASHID.decode(params[:hashid]))
并且这种模式在我们的应用程序中重复了很多次。我可以编写一些辅助函数,例如 find_by_hashid
或 where_hashid
来处理解码和可能的错误处理。但是,当将它们与其他查询方法结合使用时,这很快就会变得脆弱。
所以我想知道,是否可以扩展 ActiveRecord 查询接口以支持特殊的虚拟列 hashid
,这样就可以实现:
Product.where(hashid: ["Nkk9", "69PV"])
Product.where.not(hashid: "69PV")
Product.find_by(hashid: "Nkk9")
Product.find("Nkk9")
# store has_many :products
store.products.where(hashid: "69PV")
这个想法很简单,只需寻找 hashid
键,将其转换为 id
并解码给定的 hashid 字符串。出错时,return nil
.
但我不确定 ActiveRecord 是否提供了一种方法来做到这一点,而无需大量猴子修补。
您或许可以按照以下方式破解此基本选项,但我永远不会推荐它:
module HashIDable
module Findable
def find(*args,&block)
args = args.flatten.map! do |arg|
next arg unless arg.is_a?(String)
decoded = ::HASHID.decode(arg)
::HASHID.encode(decoded.to_i) == arg ? decoded : arg
end
super(*args,&block)
end
end
module Whereable
def where(*args)
args.each do |arg|
if arg.is_a?(Hash) && arg.key?(:hashid)
arg.merge!(id: ::HASHID.decode(arg.delete(:hashid).to_s))
end
end
super(*args)
end
end
end
ActiveRecord::FinderMethods.prepend(HashIDable::Findable)
ActiveRecord::QueryMethods.prepend(HashIDable::Whereable)
您可以将此文件放在“config/initializers”中,看看会发生什么,但此实现非常幼稚且非常脆弱。
以上可能有101个地方没有考虑到,包括但不限于:
MyModel.where("hashid = ?", "Nkk9")
MyModel.joins(:other_model).where(other_models: {hashid: "Nkk9"})
我们使用 https://github.com/peterhellberg/hashids.rb 来混淆 API:
中的数据库 IDHASHID = Hashids.new("this is my salt")
product_id = 12345
hash = HASHID.encode(product_id)
=> "NkK9"
解码哈希值时,我们必须做这样的事情:
Product.find_by(id: HASHID.decode(params[:hashid]))
并且这种模式在我们的应用程序中重复了很多次。我可以编写一些辅助函数,例如 find_by_hashid
或 where_hashid
来处理解码和可能的错误处理。但是,当将它们与其他查询方法结合使用时,这很快就会变得脆弱。
所以我想知道,是否可以扩展 ActiveRecord 查询接口以支持特殊的虚拟列 hashid
,这样就可以实现:
Product.where(hashid: ["Nkk9", "69PV"])
Product.where.not(hashid: "69PV")
Product.find_by(hashid: "Nkk9")
Product.find("Nkk9")
# store has_many :products
store.products.where(hashid: "69PV")
这个想法很简单,只需寻找 hashid
键,将其转换为 id
并解码给定的 hashid 字符串。出错时,return nil
.
但我不确定 ActiveRecord 是否提供了一种方法来做到这一点,而无需大量猴子修补。
您或许可以按照以下方式破解此基本选项,但我永远不会推荐它:
module HashIDable
module Findable
def find(*args,&block)
args = args.flatten.map! do |arg|
next arg unless arg.is_a?(String)
decoded = ::HASHID.decode(arg)
::HASHID.encode(decoded.to_i) == arg ? decoded : arg
end
super(*args,&block)
end
end
module Whereable
def where(*args)
args.each do |arg|
if arg.is_a?(Hash) && arg.key?(:hashid)
arg.merge!(id: ::HASHID.decode(arg.delete(:hashid).to_s))
end
end
super(*args)
end
end
end
ActiveRecord::FinderMethods.prepend(HashIDable::Findable)
ActiveRecord::QueryMethods.prepend(HashIDable::Whereable)
您可以将此文件放在“config/initializers”中,看看会发生什么,但此实现非常幼稚且非常脆弱。
以上可能有101个地方没有考虑到,包括但不限于:
MyModel.where("hashid = ?", "Nkk9")
MyModel.joins(:other_model).where(other_models: {hashid: "Nkk9"})