An order lives in two tables. orders gets one row for the order itself: the customer, the date and a status. order_items gets one row for each product in it: which order, which product, how many and at what price. Placing an order means writing all of these rows.
SQLite picks the id of a new row. The result of an INSERT has LastInsertId(), so you can use the id in the rows that belong to it. Rows that belong together are written in one transaction: db.Begin() starts it, tx.Exec runs each statement inside it, and tx.Commit() saves them all in shop.db. Here it is for a reviews table, if the shop had one:
tx, _ := db.Begin()
res, _ := tx.Exec("INSERT INTO reviews (product_id, stars) VALUES (?, ?)", 4, 5)
reviewID, _ := res.LastInsertId()
tx.Commit()
Each item's unit_price comes from the products table, not from the request: the client only sends product ids and quantities. tx.Commit() saves all the new rows, and the answer is 201 with the new id. An order with no items is a bad request: answer 400 and return before you start the transaction.
POST /orders {"customer_id": 5, "items": [{"product_id": 6, "quantity": 1}, {"product_id": 4, "quantity": 3}]}
→ 201 {"id": 8}
GET /orders/8/items → 200 {"items": [{"name": "Teapot", "quantity": 1, "unit_price": 22}, {"name": "Pen", "quantity": 3, "unit_price": 2}], "order_id": 8, "total": 28}
GET /orders/8 → 200 {"id": 8, "ordered": "<today's date>", "status": "new", "customer": "Eva Rossi"}
POST /orders {"customer_id": 1, "items": [{"product_id": 8, "quantity": 2}]}
→ 201 {"id": 9}
GET /orders/9/items → 200 {"items": [{"name": "Gift card", "quantity": 2, "unit_price": 50}], "order_id": 9, "total": 100}
POST /orders {"customer_id": 2, "items": []}
→ 400 {"error": "An order needs at least one item"}
Your task: the handler POST /orders, its data lines and the structs OrderLine and NewOrder are given. Start a transaction, insert the order with today's date (date('now')) and the status 'new', keep its id, add one order_items row per item with the product's current price, commit, and answer {"id": …} with 201. When Items is empty, answer 400 with the error "An order needs at least one item".
Assignment
Follow the Coding Tutorial to fill in the POST /orders handler: the transaction, the order row, its item rows, the commit, the 201 answer and the check for an empty order.
Hint
Inside the transaction, read and write through tx, not db. The empty check goes right below the Decode line, before db.Begin(), and ends with return: without it, the handler goes on and saves the empty order.
Free plan: 30 tutor messages. PRO is unlimited.
