8 - 購物車

先在 navbar加入購物車的按鈕

#web/templates/layout/navbar.html.eex
  <ul class="nav navbar-nav navbar-right">
    <li class="">
      <a href="#" class="button">Cart (0)</a>
    </li>
        ...

產生購物車以及購物車物品的 model

> mix phoenix.gen.model Cart carts
...
> mix phoenix.gen.model Cart_item cart_items cart_id:references:carts product_id:references:products

這樣 phoenix 會幫我們建立好model間的關聯,在 cart_items裡會寫好 belongs_to 的關係

接著 mix ecto.migrate 在資料庫新增這兩個model


現在有個問題:購物車該放哪?

我們把它放在 conn 裡面, conn就是進去 phoenix程式裡一條「電流」,整個程式就是 conn 導來導去,產生各種結果,在 router那邊可以看到這個電流是怎樣被導到各處

#router.ex
  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug ShoppingSite.Auth, repo: ShoppingSite.Repo
  end

可以看到我們之前已經加入 Auth這個電流關卡,現在我們再加入購物車這個電流關卡,每次一進入網站,就可以去建立購物車

#web/plug/cart_plug.ex
defmodule ShoppingSite.CartPlug do
  import Plug.Conn

  alias ShoppingSite.Repo
  alias ShoppingSite.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

#web/controllers/cart_controller.ex
defmodule ShoppingSite.CartController do
  use ShoppingSite.Web, :controller

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

之後就可以用import ShoppingSite.CartController only: [current_cart: 1] 使用 current_cart


現在就試試看,我們在 navbar上面顯示購物車的商品數量 負責 navbar呈現的 view是 layout_view.ex 在 layout_view.ex 加入

#web/views/layout_view.ex
defmodule ShoppingSite.LayoutView do
  use ShoppingSite.Web, :view

  import ShoppingSite.CartController, only: [current_cart: 1]

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

更改 navbar.html

#web/templates/layout/navbar.html.eex
<ul class="nav navbar-nav navbar-right">
    <li class="">
      <a href="#" class="button">Cart (<%= cart_item_count(@conn) %>)</a>
    </li>
    ....

刷新頁面可以看到跟以前一樣的 Cart (0)

results matching ""

    No results matching ""