solidusのproductがカートに入るまでの流れをまとめてみた
solidusのカートに入るまでの流れ
solidusのfrontend gemを参考にしてその流れをコードを追って調べてみました。
①productページ
まずこちらがsolidus frontendのproducts/showのページ
こちらのページの add to cart付近のコードをみてみるとformタグでvariant_id, quantityをorders/populateにpostメソッドで送信ていた。
②orders controller
・(a)populateでは、まず@orderに現在のorderを代入している。
・次にvariant, quantityにフォームで入力した値を代入する。
・数量が1から2147483647までの間でなければ問題が出るため、極端に値が大き場合はエラーが発生するようにしている。
・(b)先ほど定義したvariantとquantityを使ってlineItemに数量追加または新規作成し、適用条件を満たしたpromotionやshipment情報があれば追加され、orderが更新される。
・上でエラーが発生すれば、エラー情報を含めてリダイレクトされる。エラーがなければ、cart_pathにレダイレクトされる。
以上がカートに商品を追加した時の流れです。多くのmodelが絡み合っていて情報を整理したと思い記述しました!
下にさらに詳しい情報を乗せています。
※(x)で以下に,コードをさらに追った時の補足情報をつけています。
def populate
# (a)
@order = current_order(create_order_if_necessary: true)
authorize! :update, @order, cookies.signed[:guest_token]
variant = Spree::Variant.find(params[:variant_id])
quantity = params[:quantity].present? ? params[:quantity].to_i : 1
# 2,147,483,647 is crazy. See issue https://github.com/spree/spree/issues/2695.
if !quantity.between?(1, 2_147_483_647)
@order.errors.add(:base, t('spree.please_enter_reasonable_quantity'))
end
# (b)
begin
@line_item = @order.contents.add(variant, quantity)
rescue ActiveRecord::RecordInvalid => e
@order.errors.add(:base, e.record.errors.full_messages.join(", "))
end
respond_with(@order) do |format|
format.html do
if @order.errors.any?
flash[:error] = @order.errors.full_messages.join(", ")
redirect_back_or_default(spree.root_path)
return
else
redirect_to cart_path
end
end
end
end
(a)current_orderがわからなかったので、コードを追ってみると、以下の手順でorderを作成または取得していた。
①未完了のorderを探す。
find_order_by_token_or_user()で、orderのstatusがコンプリートしていないorderを探す。geust_tokenから探した後に、ユーザーログインしていたらそのユーザーの最後のコンプリートしていないorderを探す。
②新規のorderを作成(①でorderが見つからなければの場合)
options[:create_order_if_necessary]がtrueかつ@current_orderがnilのため、orderを新規作成し、current_userを作成したorderに代入
③@current_orderにipアドレスを代入し、@current_orderを返す。
@current_order.record_ip_address(ip_address)で現在のipアドレスを代入していた。仮にすでにipアドレスが存在している場合は、すでに保存されたipアドレスと現在のipアドレスが異なる場合に更新するようになっている。
def current_order(options = {})
options[:create_order_if_necessary] ||= false
return @current_order if @current_order
@current_order = find_order_by_token_or_user(options)
if options[:create_order_if_necessary] && (@current_order.nil? || @current_order.completed?)
@current_order = Spree::Order.new(new_order_params)
@current_order.user ||= try_spree_current_user
# See issue https://github.com/spree/spree/issues/3346 for reasons why this line is here
@current_order.created_by ||= try_spree_current_user
@current_order.save!
end
if @current_order
@current_order.record_ip_address(ip_address)
return @current_order
end
end
(b)@order.contents.add()がについてコードを追ってみた
このメソッドにより、LineItemの作成または既存していれば数量の追加やshipmentの追加や適用されたがあればpromotionの追加、最後に追加した情報をorderに更新していました。詳しい内容は以下の通りです。
①orderクラスのcontentsメソッドで、Spree::OrderContents.new(self)が作成され、@cotentsに代入させる。
②その@contents(Spree::OrderContentsクラスのインスタンス)に対してaddメソッドを実行している。
def contents
@contents ||= Spree::OrderContents.new(self)
end
③addメソッドをみてみると、add_to_line_itemが実行されている。add_to_line_itemでは、
まず、grab_line_item_by_variantによって、今回登録したいvariantがすでにLinItemとしてorderに登録されている場合は、そのLineItemを取り出してくる。初めて登録するvariantならnilが返る。
④続いて、上記でnilになれば、新しいLineItemを作成し、variantを追加する。
line_item.quantity += quantity.to_iにより、数量を追加。ここで既存のLineItemを使用する場合は、既存の数量に対して加算するようにしている。
⑤最後にオプションのshippmentがある場合はlineItemに追加し、保存して、LineItemを返している。
⑥返されたLineItemは、addメソッドのlien_itemに代入され、after_add_or_removeが実行される。
⑦reload_totalsメソッドでは、orderに対して現在、選択されている商品の支払い金額などを更新させ、shipmentオプションがあれば追加し、なければnilとなる。続いて、promotionが存在して、適用の条件を満たしていれば、ここで適用される。
再度reload_totalsメソッドが実行され、promotionやshipment適用後のorder更新が行われる。
最後にlineItemが返される
def add(variant, quantity = 1, options = {})
line_item = add_to_line_item(variant, quantity, options)
after_add_or_remove(line_item, options)
end
# (省略)
def add_to_line_item(variant, quantity, options = {})
line_item = grab_line_item_by_variant(variant, false, options)
line_item ||= order.line_items.new(
quantity: 0,
variant: variant,
)
line_item.quantity += quantity.to_i
line_item.options = ActionController::Parameters.new(options).permit(PermittedAttributes.line_item_attributes).to_h
line_item.target_shipment = options[:shipment]
line_item.save!
line_item
end
# (省略)
def after_add_or_remove(line_item, options = {})
reload_totals
shipment = options[:shipment]
shipment.present? ? shipment.update_amounts : order.ensure_updated_shipments
PromotionHandler::Cart.new(order, line_item).activate
reload_totals
line_item
end
参考
公式ドキュメントやソースコードなど
Author And Source
この問題について(solidusのproductがカートに入るまでの流れをまとめてみた), 我々は、より多くの情報をここで見つけました https://qiita.com/kyosuke_sumitani/items/c242b7bbbd99698f3489著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .