將 source wrapper
、 record mapper
、 factory
都完成後,在 repository
裡只需要簡單的歸位初始化後就完成了。
# 這邊以 order 做舉例,primary key 為 serial_number
class Order < Boxenn::Entity
def self.primary_keys
[:serial_number]
end
# 訂單編號 (identity)
attribute :serial_number, Types::Coercible::String
# 狀態
attribute :status, Types::Symbol.enum(successed: 0, refunded: 1, canceled: 2)
# 購買日期
attribute :puchased_at, Types::DateTime
# 備註
attribute :comment, Types::Coercible::String.optional.default(nil)
end
module Repositories
class Order < Boxenn::Repository
option :source_wrapper, default: -> { Wrapper::Order.new }
option :record_mapper, default: -> { Mapper::Order.new }
option :factory, default: -> { Factory::Order.new }
end
end
# 實際運用
repo = Repositories::Order.new
repo.find_by_identity(serial_number: 'ABC-1234567890') # 會回傳 order 的 entity
repo.destroy(serial_number: 'ABC-1234567890') # 會刪除資料庫中的 record
order = Order.new(
serial_number: 'ABC-9876543210',
status: :successed,
puchased_at: Time.zone.now,
comment: '測試',
)
repo.save(order) # 會根據 serial_number = 'ABC-9876543210' 來找 record,如果有找到即更新資料,沒有找到則建立一筆新 record
save
可以傳入多個 entity 的 array ,一次更新或新增多筆資料
負責歸納複雜讀取邏輯。
唯一定義的介面是 collect
,回傳的資料會傳入 factory 轉成 entity,以ActiveRecord
為例回傳為 array of models 即可。
下面範例一樣以 ActiveRecord
為外部資源做示範:
require 'dry/initializer'
module Boxenn
module Repositories
class Query
extend Dry::Initializer
param :relation, default: proc { nil }
def collect
raise NotImplementedError
end
end
end
end
# Order Query
class OrderQuery < Boxenn::Repositories::Query
param :relation, default: -> { ::Models::Order }
def comment_include(key_word)
@relation = relation.where('comment LIKE ?', "%#{key_word}%")
self
end
def by_status(status)
@relation = relation.where(status: status)
self
end
def collect
relation
end
end
# 實際運用
repo = Repositories::Order.new
query = OrderQuery.new
query.comment_include('測試')
repo.find_by_query(query)
需要注意這邊 query 有儲存
Active::Record::Relation
, 因此不同的 method 之間可以 chain ,但如果想要新的搜尋結果就需要重新 new 一個 query
repo = Repositories::Order.new
query = OrderQuery.new
query.comment_include('測試').by_status(:refunded)
repo.find_by_query(query)
下篇會說明如何對整個 DAL 做測試。