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)