iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 29
0
Modern Web

新時代的網頁框架比較-- 淺談Rails、Django、Phoenix、Laravel系列 第 29

Phoenix起步走:建立一個購物網站--購物車

三十天鐵人賽的倒數第二天,我想盡可能完成購物網站,但感覺有些難度。今天我會繼續進行下去,但是略過大部份的前端程式碼複製貼上,只保留重要的後端指令。關於前端的部分,可以直接參考我的github。

首先我們需要有購物車,裡面有多個購物車物件,每個物件對應一種商品:

$ mix phx.gen.schema Cart carts
$ mix phx.gen.schema CartItem cart_items cart_id:references:carts product_id:references:products

產生migrate的時候,可以直接綁定與其他model的關係:CartItem對應Cart cart_id:references:carts,CartItem對應Product product_id:references:products。記得model內需要手動修正:

# lib/shop/cart.ex
...
  schema "carts" do
    has_many :cart_items, Shop.CartItem, on_delete: :delete_all
    has_many :products, through: [:cart_items, :product]

    timestamps()
  end
...
# lib/shop/cart_item.ex
...
  schema "cart_items" do
    belongs_to :cart, Shop.Cart
    belongs_to :product, Shop.Product

    timestamps()
  end
...

記得執行migrate。接著我們新增一個新的plug來實作購物車:

# lib/shop_web/plugs/cart_plug.ex
defmodule ShopWeb.CartPlug do
  import Plug.Conn

  alias Shop.{Repo, Cart}

  def init(default), do: default

  def call(conn, _params) do
    conn
      |> find_cart
  end

  def find_cart(conn) do
    cart_id = get_session(conn, :cart_id)

    if cart_present?(cart_id) do
      case Repo.get(Cart, cart_id) do
        nil ->
          conn
            |> create_cart
        cart ->
          conn
            |> put_session(:cart_id, cart_id)
            |> assign(:current_cart, cart)
            |> configure_session(renew: true)
      end
    else
      conn
        |> create_cart
    end
  end

  def create_cart(conn) do
    changeset = Cart.changeset(%Cart{}, %{})
    case Repo.insert(changeset) do
      {:ok, cart} ->
        conn
          |> put_session(:cart_id, cart.id)
          |> assign(:current_cart, cart)
          |> configure_session(renew: true)
      {:error, _} ->
        conn
    end
  end

  def cart_present?(res) do
    case res do
      nil -> false
      _   -> true
    end
  end

end

並且寫一個cart_controller

defmodule ShopWeb.CartController do
  use ShopWeb, :controller

  def current_cart(conn) do
    conn.assigns.current_cart
  end
end

最後像處理使用者權限的auth一樣,加在router的plug內:

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug ShopWeb.Auth, repo: Shop.Repo
    plug ShopWeb.CartPlug
  end

最後在view的地方新增:

# lib/shop_web/views/layout_view.ex
defmodule ShopWeb.LayoutView do
  use ShopWeb, :view
  import ShopWeb.CartController, only: [current_cart: 1]

  def cart_item_count(conn) do
    Shop.Repo.preload(current_cart(conn), :cart_items).cart_items
      |> length
  end

end

就可以在navbar產生我們需要的購物車數量顯示了!

<li class="nav-item"><a href="#" class="nav-link">Cart (<%= cart_item_count(@conn) %>)</a></li>

從瀏覽器看起來像這樣,但目前他永遠是0,因為我們還沒有寫新增的方法。
shopping cart

參考資料


上一篇
Phoenix與Rails的比較
下一篇
比較的意義,工具有真正的優劣嗎?
系列文
新時代的網頁框架比較-- 淺談Rails、Django、Phoenix、Laravel31

尚未有邦友留言

立即登入留言