iT邦幫忙

2022 iThome 鐵人賽

DAY 26
0
Modern Web

跳脫MVC,Laravel + React 建立電商網站系列 第 26

Day 26 Laravel + React 實戰之路 -7電商轉換最後一哩路

  • 分享至 

  • xImage
  •  

我們今天的目標就是把訂單送出畫面做好,然後更新訂單資料表,我們的專案就基本完結了。

建立訂單的修改

API的驗證

但在開始之前,我們要先把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一些基本的防護了,現在要開始來寫送出訂單的後端功能~

畫面(OrderCheckOut)及路由的驗證

一樣在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吧!


上一篇
Day 25 Laravel + React 實戰之路 -7電商轉換最後一哩路
下一篇
Day 27 Reack Hook能吃嗎?
系列文
跳脫MVC,Laravel + React 建立電商網站30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言