我們今天的目標就是把訂單送出畫面做好,然後更新訂單資料表,我們的專案就基本完結了。
但在開始之前,我們要先把api修改成需要身分驗證:
我們之前有做一個驗證api格式的檔案,裡面有一個 authorize
的 function
我們需要在這邊設定授權OrderCreateRequest
public function authorize()
{
if($this->user() !== null){
return true;
}else{
return false;
}
}
api.php
Route::middleware('auth:sanctum')->post('order/create' , [\App\Http\Controllers\APIController::class,'createOrder'])->name('create-order');
ps.如果有遇到Unauthenticated
之類的問題,多半是.env
或相關設定檔問題,上網搜尋一下多加嘗試就ok了。
然後我們在createOrder
的地方加一個身分判斷,避免無條件相信前端傳到後端的資料:
if(Auth::user()->getAuthIdentifier() === $post_data['customer_id']){
$create_order = Order::create([
'number' => (string)Str::uuid(),
'user_id' => $post_data['customer_id'],
'type' => OrderConst::UNCHECK,
'phone_number' => '尚未成立',
'address' => '尚未成立',
'price' => (int)$tmp_price * $post_data['amount']
]);
OrderDetail::create([
'order_id' => $create_order->id,
'product_id' => $post_data['product'],
'amount' => $post_data['amount'],
'price' => $tmp_price
]);
return $create_order->number;
}
return "身分驗證失敗";
ok,我們現在給了api一些基本的防護了,現在要開始來寫送出訂單的後端功能~
一樣在router的地方加入一個身分驗證的middleware,如果沒有登入的話直接拒絕訪問。
web.php
Route::get('order/send/{order_number?}' , [\App\Http\Controllers\OrderController::class,'send'])
->middleware(['auth', 'verified'])->name('send-order');
OrderController.php
public function send($order_number)
{
$order = Order::where('number',$order_number)->where('type' , OrderConst::UNCHECK)->where('user_id' , Auth::user()->getAuthIdentifier())->first();
if(is_null($order)){
abort('404');
}
dd("成功進入訂單修改畫面");
}
如果照著正常的流程走,就可以看到 "成功進入訂單修改畫面"的字樣。
如果 訂單編號、使用者跟訂單狀態不符合 就會被丟到404頁面,以此來拒絕不符的使用者操作。
接下來回到OrderModel來建立關聯,照理來原本設計的資料庫規則是一(Order)對多(OrderDetail)
但因後篇幅問題修改了一下畫面流程,所以實際上的關聯變成一對一了(取消購物車就沒辦法多商品)Order.php
public function details()
{
return $this->hasMany(OrderDetail::class);
}
ps.然後OrderDetail 也要設定一(OrderDetail)對一(Product)的關聯
然後重新在撈資料的地方加個關聯:
$order = Order::with('details','details.products')
->where('number',$order_number)->where('type' , OrderConst::UNCHECK)
->where('user_id' , Auth::user()->getAuthIdentifier())
->first();
最後return Inertia 的地方改成這樣:
return Inertia::render('OrderCheckOut' , [
'order' => $order,
'productName' => $order->details->first()->products->name,
'price' => $order->details->first()->price, //成立訂單當下的商品金額,不能拿現在的
'amount' => $order->details->first()->amount
]);
這樣我們基本的後端就又建立好了!
接下來我們到前端畫面,我們一樣先手刻畫面出來:
這裡要丟資料給後端去更新資料表,可以參考使用Form Post的方式;因為上次用axios,所以我這次就用form post 練習看看。
整個前端的code最後會是這樣:OrderCheckOut.jsx(訂單送出畫面)
import React, {useEffect} from "react";
import Label from "@/Components/Label";
import Input from "@/Components/Input";
import {Link, useForm} from "@inertiajs/inertia-react";
import ApplicationLogo from "@/Components/ApplicationLogo";
import Button from "@/Components/Button";
export default function Home(props){
const { data, setData, post, processing, errors, reset } = useForm({
orderNumber:props.order.number,
phoneNumber: '',
address: ''
});
const onHandleChange = (event) => {
setData(event.target.name, event.target.value);
};
const submit = (e) => {
e.preventDefault();
post(route('order-checkout'));
};
return(
<div className="min-h-screen flex flex-col sm:justify-center
items-center pt-6 sm:pt-0 bg-gray-100">
<div>
<Link href="/">
<ApplicationLogo className="w-20 h-20 fill-current text-gray-500" />
</Link>
</div>
<div className="w-full sm:max-w-md mt-6 px-6 py-4 bg-white
shadow-md overflow-hidden sm:rounded-lg">
<form onSubmit={submit}>
<div>
<Label value="本次購買的商品:" />
<p style={{margin:"16px"}}>{props.productName}</p>
</div>
<div>
<Label value="商品單價:" />
<p style={{margin:"16px"}}>{props.price}</p>
</div>
<div>
<Label forInput="phoneNumber" value="電話號碼" />
<Input
type="text"
name="phoneNumber"
value={data.phoneNumber}
className="mt-1 block w-full"
autoComplete="phoneNumber"
isFocused={true}
handleChange={onHandleChange}
required
/>
</div>
<div>
<Label forInput="address" value="運送地址" />
<Input
type="text"
name="address"
value={data.address}
className="mt-1 block w-full"
autoComplete="address"
isFocused={true}
handleChange={onHandleChange}
required
/>
</div>
<div>
<Label forInput="amount" value="商品數量(最後確認)" />
<p style={{margin:"16px"}}>{props.amount}</p>
</div>
<div>
<Label value="訂單最後金額" />
<p style={{margin:"16px"}}>{props.order.price}</p>
</div>
<Button className="ml-4">
送出訂單
</Button>
</form>
</div>
</div>
);
}
OrderController 新增一個function,註冊路由的部分我就不多贅述~
public function check(Request $request)
{
dd($request->all());
}
可以透過 $request 來獲得 從前端 FormPost 過來的資料。
礙於篇幅 我這邊就直接更新資料表(因為我懶了QQ)
然後就用最後這個 "完成下單" 的動作來當成這個專案的Ending~
public function check(Request $request)
{
$post_data = $request->all();
Order::where('number' , $post_data['orderNumber'])->where('type' , OrderConst::UNCHECK)
->where('user_id' , Auth::user()->getAuthIdentifier())
->update([
'type' => OrderConst::CHECKOUT ,
'phone_number' => $post_data['phoneNumber'] ,
'address' => $post_data['address']
]);
return "下單成功";
}
如此一來 也成功更新回Order的資料表了~ 終於完成了這個簡陋的電商專案,可喜可賀!!
接下來還有四天,明天來繼續React 的部分,就談談甚麼是Hook吧!