1

我们使用https://github.com/peterhellberg/hashids.rb在我们的 API 中混淆数据库 ID:

HASHID = Hashids.new("this is my salt")

product_id = 12345
hash = HASHID.encode(product_id)
=> "NkK9"

在解码 hashids 时,我们必须这样做:

Product.find_by(id: HASHID.decode(params[:hashid]))

这种模式在我们的应用程序中重复了很多次。我可以编写一些辅助函数,例如find_by_hashidwhere_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 字符串。出错时,返回nil

但我不确定 ActiveRecord 是否提供了一种方法来做到这一点,而无需大量的猴子补丁。

4

1 回答 1

1

您可能可以按以下方式破解此基本选项,但我永远不会推荐它:

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"})
于 2021-05-13T19:18:21.917 回答