[v] xdite rails 第二週 hw

消費者可用「清空購物車功能」來手動清空購物車

  • 如果是用砸爛購物車的方式來清空購物車,是否是砸爛之後再找一台購物車給user的概念?

是的!sudo code 是 destroy cart。如果是用移除購物車內的商品的方式,sudo code 是 destroy_all cart items

  • 為什麼是 cart_items.destroy_all ?
cart.rb
  def clean!
    cart_items.destroy_all
  end
  • 拆解 if @item.product.quantity >= item_params[:quantity].to_i

item_params[:quantity] 是我們選擇好數量、按下"送出"鈕當下的那個數量,:quantity 其實是 hash key, item_params[:quantity] 是要取 hash value, 這個 value 是 string, 後面用 to_i 轉成 integer。

消費者可以「手動」從購物車內刪掉某些物品

  • 拆解 resources :items, controller: "cart_items"

items 是自己定義的,實際上沒 items_controller, 所以後面要指定,而我們指定這是 cart_items controller

  • [提問] 為什麼是指定 controller? 一定要指定 cart_items controller 嗎?
  • 之前就有 cart_item model 了,到這裡才補上 cart_items controller, 可以這樣嗎?

可以。model 和 controller 不是有一個就非要有另一個。

  • 下方何解?
<%= link_to item_path(product), method: :delete do %>
  <i class="fa fa-trash"></i>
<% end %>

一般我們使用 link_to 是像:

link_to "XXX", item_path(product), method: :get

method: :get 可以省略 (其他 http verbs 不能省),於是寫成:

link_to "XXX", item_path(product)

我們在網頁上看到的,則是一個顯示為 "XXX" 文字的 link

而教材上的寫法:

link_to item_path(product), method: :delete do    # 直接接 路徑 和 method
  something                                       # 要宣告的內容,比如一段 html code
end

我們不想顯示什麼文字 link,而是想要顯示其他 html 的 code,就可以用這種方式去宣告。

這時,前端網頁上看到的,是一個 垃圾桶 icon, 點下去就是對 item_path(product) 執行 delete 的動作,也就是把 product 給刪除了。

  • "點下去就是對 item_path(product) 執行 delete 的動作,也就是把 product 給刪除了。" 這句話是對的嗎?

對!

  • <i class="fa fa-trash"></i> 為什麼使用 <i> tag?
  • 在這個例子中,是宣告一個垃圾桶 icon, 那如果要宣告其他的東西,比如說一張圖片,要怎麼做?

都是寫在 <a> tag 裡面,就用 html 的正常寫法寫就好。

  • 拆解下面 code block 中的 @item = @cart.cart_items.find_by(product_id: params[:id]) 以及 @product = @item.product
cart_items_controller.rb
class CartItemsController < ApplicationController
  before_action :authenticate_user!

  def destroy
    @cart = current_cart
    @item = @cart.cart_items.find_by(product_id: params[:id])
    @product = @item.product
    @item.destroy

    flash[:warning] = "成功將 #{@product.title} 從購物車刪除!"
    redirect_to :back
  end
end

購物車加入「數量」設計 ( 基礎設定 )

  • 下面的 item, cart_item, product 搞不清楚它們之間的關係。另外,<%= form_for item, url: item_path(product) do |f| %> 中為什麼要寫 url?
app/views/carts/index.html.erb
+           < td>
+             <% item = current_cart.cart_items.find_by(product_id: product) %>
+             <%= form_for item, url: item_path(product) do |f| %>
+               <%= f.select :quantity, [1,2,3,4,5] %>
+               <%= f.submit "更新", data: { disable_with: "Submiting..." } %>
+             <% end %>
+           </td>

購物車加入「數量」設計 ( 總價 => 小計 x 數量 )

  • 為什麼要把 items 改成 cart_items?
app/models/cart.rb
  def total_price
    sum = 0

-   items.each do |item|
-     sum = sum + item.price
+     cart_items.each do |cart_item|
+       sum = sum + (cart_item.product.price * cart_item.quantity)
      end

      sum
    end
  end

購物車加入「數量」設計 ( 數量為 0 的貨物不能「購買」)

  • 下方 item_params[:quantity].to_i 是什麼?為什麼要用它?
app/controllers/cart_items_controller.rb
  def update
    @cart = current_cart
    @item = @cart.find_cart_item(params[:id])

+   if @item.product.quantity >= item_params[:quantity].to_i
      @item.update(item_params)
+     flash[:notice] = "成功變更數量"
+   else
+     flash[:warning] = "數量不足以加入購物車"
+   end

    redirect_to carts_path
  end




購物車加入「數量」設計 ( 購物車不能手動更新超過庫存的數量 )

n/a



訂單產生時,要把購物車的商品數量存進去

  • 下方的 item.quantity = cart.cart_items.find_by(product_id: cart_item).quantity 何解?
app/models/order.rb
  def build_item_cache_from_cart(cart)
    cart.items.each do |cart_item|
      item = items.build
      item.product_name = cart_item.title
      item.quantity = cart.cart_items.find_by(product_id: cart_item).quantity
      item.price = cart_item.price
      item.save
    end
  end




產生訂單後,購物車應該被清空
修改訂單頁面 ( 增加數量欄位 )

n/a



建立 account/orders 可以看到該使用者過去所有訂單

  • order.created_at 是 UTC 0 的時間,所以我們在訂單列表看到的時間會比 UTC+8 的台灣慢 8 小時。




結賬後跳轉到 account/orders#index

n/a



建立 admin/orders 可以看到系統內所有訂單

  • 拆解 @orders = Order.order("id DESC”) 為什麼不是@orders = Order.all.order(“id DESC”)?

都可以。前面的寫法是 rails model 那邊的 magic

  • <th>link_to order.id, admin_order_path(order) </th> 為什麼不用吃 order.token 當參數?如果今天不是EC網站,這樣會不會有隱私問題?

admin 的 order 列表應要能顯示訂單狀態

  • 拆解 t("orders.order_state.#{order.aasm_state}”) config/locales/zh-TW.yml 中的 orders: 和 order_state: 又是代表什麼?

https://ihower.tw/rails4/i18n.html
t 是一個 i18n helper

module OrdersHelper
  def render_order_state(order)
    t("orders.order_state.#{order.aasm_state}")
  end
end
zh-TW:
  orders:
    order_state:
      order_placed: 已下單
      paid: 已付款
      shipping: 出貨中
      shipped: 已到貨
      order_cancelled: 取消訂單
      good_returned: 退貨

其實是看 t("orders.order_state.#{order.aasm_state}") 的結構
最後面的 #{order.aasm_state} <== 代表該訂單 aasm_state 欄位的資料 , 我們在 order.rb 裡面將該訂單設定了六個狀態,會依照不同狀態,把英文轉成中文。

  • 怎麼使用 i18n 及 yml 檔來轉換語言?

兩種作法:

第一種是直接在 order_info.rb 那邊改 message

class OrderInfo < ActiveRecord::Base
  belongs_to :order
  validates :billing_name,     presence: { message: I18n.t("can_not_be_blank") }
  validates :billing_address,  presence: { message: "Can not be blank!" }
  validates :shipping_name,    presence: true
  validates :shipping_address, presence: true
end

或是在 zh-TW.yml 那邊改 message

zh-TW:
  orders:
    order_state:
      order_placed: 已下單
      paid: 已付款
      shipping: 出貨中
      shipped: 已到貨
      order_cancelled: 取消訂單
      goods_returned: 退貨
  can_not_be_blank: 不能空白喔!

第二種是按照他的 message 的邏輯建一個 i18n 設定檔

zh-TW:
  orders:
    order_state:
      order_placed: 已下單
      paid: 已付款
      shipping: 出貨中
      shipped: 已到貨
      order_cancelled: 取消訂單
      goods_returned: 退貨
  can_not_be_blank: 不可以空白
  activerecord:
    errors:
      models:
        order_info:
          attributes:
            shipping_address:
              blank: 不可以空白
            shipping_name:
              blank: 不可以空白
            billing_name:
              blank: 不可以空白
            billing_address:
              blank: 不可以空白

兩種方法都寫的話,第二種會優先於第一種來顯示。

  • 上題的 i18n 設定檔,就是指 zh-TW.yml 這種嗎?還是在 config/locales 裡面的檔案都算?

config/locales 裡面的檔案都算

  • message 的邏輯要怎麼查?

可以從錯誤訊息看到一長串的路徑,這就是 message 的邏輯。

  • [提問] 有沒有其他方式,比如在 terminal 的指令,能夠列出這樣的邏輯?
  • 怎麼依照 IP 或是地理位置來轉換語言?

https://github.com/alexreisner/geocoder
http://www.sitepoint.com/go-global-rails-i18n/
http://edgeguides.rubyonrails.org/i18n.html
http://blog.emiketic.com/web/2015/12/06/using-rails-variants-for-custom-device-templating-and-localisation.html

  • 怎麼依照系統語言來選擇網站顯示的語言?

http://guides.rubyonrails.org/i18n.html#setting-the-locale-from-the-client-supplied-information

單張 order 應要可以依「按照狀態圖」改變狀態

  • .ship! .deliver! .cancel_order! .return_goods! 這些有 bang 的 methods 是 aasm 自動產生的嗎?

是的

  • 下面這段中,@order = Order.find(params[:id] 在調整為使用 token 後,是不是應該改成 @order = Order.find_by_token(params[:id])?
app/controllers/admin/orders_controller.rb
def show
  @order = Order.find(params[:id])
  @order_info = @order.info
  @order_items = @order.items
end

如果是在 admin 層級,不需要是 Order.find_by_token(params[:id]。

  • 承上,@order.items 的部分,@order 怎麼和 items 產生關聯的?是因為 Order model 中的 has_many :items, class_name: "OrderItem", dependent: :destroy 嗎?
  • 拆解 <%= render "admin/orders/state_option", order: @order %>

EPIC

  • 購物車內沒東西還是可以結帳

  • 出貨後無法自動減少庫存數

  • 後台無法刪除產品