You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
from(i inCartItem, where: i.cart_id ==^cart.id and i.quantity ==0)
552
+
553
+
Repo.transact(fn->
554
+
with {:ok, cart} <-Repo.update(changeset),
555
+
{_count, _cart_items} =Repo.delete_all(from(i inCartItem, where: i.cart_id ==^cart.id and i.quantity ==0)) do
556
+
{:ok, cart}
557
+
end
557
558
end)
558
-
|>Repo.transact()
559
559
|>casedo
560
-
{:ok, %{cart: cart}} ->
560
+
{:ok, cart} ->
561
561
broadcast_cart(scope, {:updated, cart})
562
562
{:ok, cart}
563
563
564
-
{:error, :cart, changeset, _changes_so_far} ->
565
-
{:error, changeset}
564
+
{:error, reason} ->
565
+
{:error, reason}
566
566
end
567
567
end
568
568
```
569
569
570
-
We started much like how our out-of-the-box code started – we take the cart struct and cast the user input to a cart changeset, except this time we use `Ecto.Changeset.cast_assoc/3` to cast the nested item data into `CartItem` changesets. Remember the [`<.inputs_for />`](https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#inputs_for/1) call in our cart form template? That hidden ID data is what allows Ecto's `cast_assoc` to map item data back to existing item associations in the cart. Next we use `Ecto.Multi.new/0`, which you may not have seen before. Ecto's `Multi` is a feature that allows lazily defining a chain of named operations to eventually execute inside a database transaction. Each operation in the multi chain receives the values from the previous steps and executes until a failed step is encountered. When an operation fails, the transaction is rolled back and an error is returned, otherwise the transaction is committed.
570
+
We started much like how our out-of-the-box code started – we take the cart struct and cast the user input to a cart changeset, except this time we use `Ecto.Changeset.cast_assoc/3` to cast the nested item data into `CartItem` changesets. Remember the [`<.inputs_for />`](https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#inputs_for/1) call in our cart form template? That hidden ID data is what allows Ecto's `cast_assoc` to map item data back to existing item associations in the cart.
571
571
572
-
For our multi operations, we start by issuing an update of our cart, which we named `:cart`. After the cart update is issued, we perform a multi `delete_all` operation, which takes the updated cart and applies our zero-quantity logic. We prune any items in the cart with zero quantity by returning an ecto query that finds all cart items for this cart with an empty quantity. Calling `Repo.transact/1` with our multi will execute the operations in a new transaction and we return the success or failure result to the caller just like the original function.
572
+
Once the `changeset` is ready, we wrap everything in `Repo.transact/2` so the operations run safely as one. Inside the transaction, we update the cart using `Repo.update/1`. If the update succeeds, we follow up with a cleanup step using `Repo.delete_all/2` to remove any cart items with zero quantity. Running both steps in the same transaction prevents partial updates and keeps the cart data accurate. Finally, we broadcast the updated cart so that any connected LiveViews can instantly show the changes.
573
573
574
574
Let's head back to the browser and try it out. Add a few products to your cart, update the quantities, and watch the values changes along with the price calculations. Setting any quantity to 0 will also remove the item. You can also try logging out and registering a new user to see how the carts are scoped to the current user. Pretty neat!
{:ok, _cart} <-ShoppingCart.prune_cart_items(scope, cart) do
227
+
{:ok, order}
228
+
end
224
229
end)
225
-
|>Repo.transact()
226
230
|>casedo
227
-
{:ok, %{order: order}} ->
231
+
{:ok, order} ->
228
232
broadcast_order(scope, {:created, order})
229
233
{:ok, order}
230
-
231
-
{:error, name, value, _changes_so_far} ->
232
-
{:error, {name, value}}
234
+
235
+
{:error, reason} ->
236
+
{:error, reason}
233
237
end
234
238
end
235
239
```
236
240
237
-
We started by mapping the `%ShoppingCart.CartItem{}`'s in our shopping cart into a map of order line items structs. The job of the order line item record is to capture the price of the product *at payment transaction time*, so we reference the product's price here. Next, we create a bare order changeset with `Ecto.Changeset.change/2` and associate our user UUID, set our total price calculation, and place our order line items in the changeset. With a fresh order changeset ready to be inserted, we can again make use of `Ecto.Multi` to execute our operations in a database transaction. We start by inserting the order, followed by a `run` operation. The `Ecto.Multi.run/3` function allows us to run any code in the function which must either succeed with `{:ok, result}` or error, which halts and rolls back the transaction. Here, we simply call into our shopping cart context and ask it to prune all items in a cart. Running the transaction will execute the multi as before and we return the result to the caller.
241
+
We started by mapping the `%ShoppingCart.CartItem{}`'s in our shopping cart into a map of order line items structs. The job of the order line item record is to capture the price of the product *at payment transaction time*, so we reference the product's price here. Next, we create a bare order changeset with `Ecto.Changeset.change/2` and associate our user UUID, set our total price calculation, and place our order line items in the changeset. With a fresh order changeset ready to be inserted, we now make use of `Repo.transact/2` to execute our operations in a database transaction. We start by inserting the order, followed by a step that prunes all items from the user’s cart. The function wrapped inside `Repo.transact/2`must either return `{:ok, result}` or error, which halts and rolls back the transaction. Running the transaction will execute these operations in sequence, and we return the result to the caller once completed.
238
242
239
243
To close out our order completion, we need to implement the `ShoppingCart.prune_cart_items/1` function in `lib/hello/shopping_cart.ex`:
0 commit comments