iT邦幫忙

2026 iThome 鐵人賽

DAY 30
0
自我挑戰組

Laravel 讀碼源系列 第 30

美髮預約系統_customers的MVC內容

  • 分享至 

  • xImage
  •  

https://laihao.vaserver.com/customers

Model內容

<?php

namespace App\Models\Hairdressing;

use Illuminate\Database\Eloquent\Model;

class Customer extends Model
{
    protected $connection = 'mysql_hairdressing';
    protected $table = 'customers';
    protected $primaryKey = 'id';
    public $timestamps = true;

    protected $fillable = [
        'name',
        'phone',
        'email',
        'gender',
        'birthday',
        'notes',
        'allergy_notes',
        'hair_notes',
        'is_member',
        'member_no',
    ];

    protected $casts = [
        'birthday'  => 'date',
        'is_member' => 'boolean',
    ];

    public function appointments()
    {
        return $this->hasMany(Appointment::class, 'customer_id');
    }

    public function invoices()
    {
        return $this->hasMany(Invoice::class, 'customer_id');
    }
}

這段程式碼是一個 Laravel Eloquent Model,用來對應前面 SQL 裡的 customers 資料表,並定義與 appointmentsinvoices 的一對多關係。下面逐行說明。 api.laravel

<?php
  • PHP 開頭標籤,表示這是一個 PHP 檔案。 api.laravel
namespace App\Models\Hairdressing;
  • 定義命名空間(namespace),這個類別位於 App\Models\Hairdressing 之下,方便在專案中以 App\Models\Hairdressing\Customer 引用。 api.laravel
use Illuminate\Database\Eloquent\Model;
  • 引入 Laravel 的 Eloquent Model 基底類別,之後的 Customer 會繼承它,獲得 ORM 功能(查詢、建立、更新、刪除等)。 api.laravel
class Customer extends Model
{
  • 定義 Customer 類別,繼承自 Model,代表這是一個 Eloquent 模型,對應資料庫中的資料表。 api.laravel
    protected $connection = 'mysql_hairdressing';
  • 指定這個模型要使用的資料庫連線名稱,這裡是 mysql_hairdressing(需在 config/database.php.env 中定義過)。 api.laravel
  • 若沒指定,Laravel 預設使用 mysql 連線。 api.laravel
    protected $table = 'customers';
  • 明確指定模型對應的資料表名稱為 customersapi.laravel
  • 若沒指定,Laravel 會依類別名稱自動推斷為 customers(小寫 + 複數),這裡是明確寫出。 api.laravel
    protected $primaryKey = 'id';
  • 指定主鍵欄位名稱為 idapi.laravel
  • 這也是 Eloquent 的預設值,這裡是明確宣告。 api.laravel
    public $timestamps = true;
  • 告訴 Eloquent 這個資料表有 created_atupdated_at 欄位,需要自動維護時間戳記。 api.laravel
  • 對應你 SQL 中的 created_atupdated_at 欄位。 api.laravel
    protected $fillable = [
        'name',
        'phone',
        'email',
        'gender',
        'birthday',
        'notes',
        'allergy_notes',
        'hair_notes',
        'is_member',
        'member_no',
    ];
  • 定義「可大量指派(mass assignable)」的欄位白名單。 api.laravel
  • 使用 create()fill() 等方法時,只有這裡列出的欄位可以透過陣列一次帶入,避免安全風險(例如意外更新 id 或其他敏感欄位)。 api.laravel
  • 注意:idcreated_atupdated_at 不在這裡,因為它們由資料庫或 Eloquent 自動管理。 api.laravel
    protected $casts = [
        'birthday'  => 'date',
        'is_member' => 'boolean',
    ];
  • 定義屬性轉型(casts),讓 Eloquent 在讀取/寫入時自動轉換型別。 laravel
  • 'birthday' => 'date':把資料庫的 date 欄位轉成 Carbon 日期物件,方便用 $customer->birthday->format('Y-m-d') 等操作。 laravel
  • 'is_member' => 'boolean':把資料庫的 tinyint(1) 轉成 PHP 的 true/false,例如 $customer->is_member 會回傳布林值。 laravel
    public function appointments()
    {
        return $this->hasMany(Appointment::class, 'customer_id');
    }
  • 定義一個「一對多」關係:一個顧客可以有多筆預約(appointments)。 api.laravel
  • hasMany(Appointment::class, 'customer_id') 表示:
    • 關聯的模型是 Appointment(需在同一命名空間或加上完整 namespace)。
    • 外鍵欄位是 appointments 表中的 customer_id,指向本表的 id(主鍵)。 api.laravel
  • 使用時可以寫:$customer->appointments 取得該顧客所有預約的 Collection。 api.laravel
    public function invoices()
    {
        return $this->hasMany(Invoice::class, 'customer_id');
    }
}
  • 同樣定義「一對多」關係:一個顧客可以有多張發票/收據(invoices)。 api.laravel
  • hasMany(Invoice::class, 'customer_id') 表示:
    • 關聯的模型是 Invoice
    • 外鍵欄位是 invoices 表中的 customer_idapi.laravel
  • 使用時可以寫:$customer->invoices 取得該顧客所有發票的 Collection。 api.laravel
<?php

namespace App\Http\Controllers;

use App\Models\Hairdressing\Customer;
use Illuminate\Http\Request;

class CustomerController extends Controller
{
    public function index()
    {
        $customers = Customer::orderBy('created_at', 'desc')->get();

        return view('hairdressing.customers.index', ['customers' => $customers]);
    }

    public function create()
    {
        return view('hairdressing.customers.create');
    }

    public function store(Request $request)
    {
        $request->validate([
            'name'          => 'required|max:100',
            'phone'         => 'required|max:20',
            'email'         => 'nullable|email|max:150',
            'gender'        => 'nullable|in:male,female,other',
            'birthday'      => 'nullable|date',
            'notes'         => 'nullable|max:255',
            'allergy_notes' => 'nullable|max:255',
            'hair_notes'    => 'nullable|max:255',
            'is_member'     => 'nullable|boolean',
            'member_no'     => 'nullable|max:20|unique:mysql_hairdressing.customers,member_no',
        ]);

        Customer::create([
            'name'          => $request->name,
            'phone'         => $request->phone,
            'email'         => $request->email,
            'gender'        => $request->gender,
            'birthday'      => $request->birthday,
            'notes'         => $request->notes,
            'allergy_notes' => $request->allergy_notes,
            'hair_notes'    => $request->hair_notes,
            'is_member'     => $request->has('is_member'),
            'member_no'     => $request->member_no,
        ]);

        return redirect()->route('customers.index')->with('success', '顧客新增成功!');
    }

    public function edit(Customer $customer)
    {
        return view('hairdressing.customers.edit', ['customer' => $customer]);
    }

    public function update(Request $request, Customer $customer)
    {
        $request->validate([
            'name'          => 'required|max:100',
            'phone'         => 'required|max:20',
            'email'         => 'nullable|email|max:150',
            'gender'        => 'nullable|in:male,female,other',
            'birthday'      => 'nullable|date',
            'notes'         => 'nullable|max:255',
            'allergy_notes' => 'nullable|max:255',
            'hair_notes'    => 'nullable|max:255',
            'is_member'     => 'nullable|boolean',
            'member_no'     => 'nullable|max:20|unique:mysql_hairdressing.customers,member_no,' . $customer->id,
        ]);

        $customer->update([
            'name'          => $request->name,
            'phone'         => $request->phone,
            'email'         => $request->email,
            'gender'        => $request->gender,
            'birthday'      => $request->birthday,
            'notes'         => $request->notes,
            'allergy_notes' => $request->allergy_notes,
            'hair_notes'    => $request->hair_notes,
            'is_member'     => $request->has('is_member'),
            'member_no'     => $request->member_no,
        ]);

        return redirect()->route('customers.index')->with('success', '顧客資料更新成功!');
    }

    public function destroy(Customer $customer)
    {
        $customer->delete();

        return redirect()->route('customers.index')->with('success', '顧客已刪除!');
    }
}

這是一個典型的 Laravel CRUD Controller,用來管理顧客資料(列表、新增、編輯、更新、刪除)。下面逐段、逐行說明程式碼的作用。 api.laravel

<?php
  • PHP 開頭標籤,表示這是 PHP 檔案。 api.laravel
namespace App\Http\Controllers;
  • 定義命名空間,這個類別位於 App\Http\Controllers 下,符合 Laravel 預設的 Controller 位置。 api.laravel
use App\Models\Hairdressing\Customer;
use Illuminate\Http\Request;
  • 引入 Customer 模型,之後可以用 Customer:: 呼叫 Eloquent 方法。 api.laravel
  • 引入 Request 類別,用來取得表單與請求的資料、進行驗證等。 api.laravel
class CustomerController extends Controller
{
  • 定義 CustomerController 類別,繼承自 Laravel 的基礎 Controllerapi.laravel

列表頁面:index()

    public function index()
    {
        $customers = Customer::orderBy('created_at', 'desc')->get();

        return view('hairdressing.customers.index', ['customers' => $customers]);
    }
  • index():對應顧客列表頁面的方法。 api.laravel
  • Customer::orderBy('created_at', 'desc')->get()
    • customers 資料表查詢所有資料。
    • 依照 created_at 欄位由新到舊排序(desc)。
    • get() 回傳一個 Eloquent Collection。 api.laravel
  • view('hairdressing.customers.index', ['customers' => $customers])
    • 回傳 resources/views/hairdressing/customers/index.blade.php 視圖。
    • $customers 傳進視圖,在 Blade 中可以用 $customers 遍歷顯示。 api.laravel

新增表單頁面:create()

    public function create()
    {
        return view('hairdressing.customers.create');
    }
  • create():回傳「新增顧客」的表單頁面。 api.laravel
  • 視圖路徑:resources/views/hairdressing/customers/create.blade.phpapi.laravel
  • 這裡沒有傳資料,因為是空白表單。 api.laravel

儲存新增資料:store()

    public function store(Request $request)
    {
  • store():處理新增表單提交後的邏輯,接收 Request 物件。 api.laravel

表單驗證

        $request->validate([
            'name'          => 'required|max:100',
            'phone'         => 'required|max:20',
            'email'         => 'nullable|email|max:150',
            'gender'        => 'nullable|in:male,female,other',
            'birthday'      => 'nullable|date',
            'notes'         => 'nullable|max:255',
            'allergy_notes' => 'nullable|max:255',
            'hair_notes'    => 'nullable|max:255',
            'is_member'     => 'nullable|boolean',
            'member_no'     => 'nullable|max:20|unique:mysql_hairdressing.customers,member_no',
        ]);
  • $request->validate([...]):對表單資料進行驗證,若失敗會自動導回並帶入錯誤訊息。 api.laravel
  • 各欄位規則說明:
    • 'name' => 'required|max:100':必填,最多 100 字,對應資料庫 varchar(100)api.laravel
    • 'phone' => 'required|max:20':必填,最多 20 字,對應 varchar(20)api.laravel
    • 'email' => 'nullable|email|max:150':可為空,若有值必須是合法 email,最多 150 字。 api.laravel
    • 'gender' => 'nullable|in:male,female,other':可為空,若有值必須是 malefemaleother,對應 enumapi.laravel
    • 'birthday' => 'nullable|date':可為空,若有值必須是合法日期格式。 api.laravel
    • 'notes', 'allergy_notes', 'hair_notes':可為空,最多 255 字(你 SQL 中是 text,這裡用 255 做前端限制)。 api.laravel
    • 'is_member' => 'nullable|boolean':可為空,必須是布林值(表單通常用 checkbox)。 api.laravel
    • 'member_no' => 'nullable|max:20|unique:mysql_hairdressing.customers,member_no'
      • 可為空,最多 20 字。
      • unique:mysql_hairdressing.customers,member_no 表示在 mysql_hairdressing 連線的 customers 表中,member_no 必須唯一。 api.laravel

寫入資料庫

        Customer::create([
            'name'          => $request->name,
            'phone'         => $request->phone,
            'email'         => $request->email,
            'gender'        => $request->gender,
            'birthday'      => $request->birthday,
            'notes'         => $request->notes,
            'allergy_notes' => $request->allergy_notes,
            'hair_notes'    => $request->hair_notes,
            'is_member'     => $request->has('is_member'),
            'member_no'     => $request->member_no,
        ]);
  • Customer::create([...]):使用大量指派(mass assignment)建立一筆新顧客資料。 api.laravel
  • 各欄位直接從 $request 取值:
    • $request->name$request->phone 等對應表單欄位名稱。 api.laravel
    • 'is_member' => $request->has('is_member')
      • 若表單有送出 is_member(例如 checkbox 被勾選),則為 true,否則 falseapi.laravel
      • 這樣處理是因為 HTML checkbox 未勾選時不會送到伺服器。 api.laravel
  • 這裡沒手動設 created_atupdated_at,Eloquent 會自動填入。 api.laravel

導回列表頁

        return redirect()->route('customers.index')->with('success', '顧客新增成功!');
    }
  • redirect()->route('customers.index'):導向名為 customers.index 的路由(通常是顧客列表頁)。 api.laravel
  • ->with('success', '顧客新增成功!'):在 session 中暫存一則成功訊息,可在視圖中用 session('success') 顯示。 api.laravel

編輯表單頁面:edit()

    public function edit(Customer $customer)
    {
        return view('hairdressing.customers.edit', ['customer' => $customer]);
    }
  • edit():回傳「編輯顧客」的表單頁面。 api.laravel
  • 參數 Customer $customer 使用路由模型綁定(Route Model Binding):
    • 若路由定義為 Route::get('/customers/{customer}/edit', ...),Laravel 會自動根據 {customer} 的 id 查詢對應的 Customer 模型。 api.laravel
  • 視圖路徑:resources/views/hairdressing/customers/edit.blade.phpapi.laravel
  • $customer 傳進視圖,Blade 中可用來顯示目前資料並填入表單。 api.laravel

更新資料:update()

    public function update(Request $request, Customer $customer)
    {
  • update():處理編輯表單提交後的更新邏輯。 api.laravel
  • 接收 Request 與要更新的 $customer 模型(同樣使用路由模型綁定)。 api.laravel

表單驗證(更新用)

        $request->validate([
            'name'          => 'required|max:100',
            'phone'         => 'required|max:20',
            'email'         => 'nullable|email|max:150',
            'gender'        => 'nullable|in:male,female,other',
            'birthday'      => 'nullable|date',
            'notes'         => 'nullable|max:255',
            'allergy_notes' => 'nullable|max:255',
            'hair_notes'    => 'nullable|max:255',
            'is_member'     => 'nullable|boolean',
            'member_no'     => 'nullable|max:20|unique:mysql_hairdressing.customers,member_no,' . $customer->id,
        ]);
  • 驗證規則與 store() 大部分相同,差別在 member_no 的唯一性檢查。 api.laravel
  • 'member_no' => '...unique:mysql_hairdressing.customers,member_no,' . $customer->id
    • 檢查 member_nocustomers 表中是否唯一,但排除目前這筆資料($customer->id)。 api.laravel
    • 這樣才能允許「不修改 member_no」或「改回原本的值」時不會誤判為重複。 api.laravel

更新資料庫

        $customer->update([
            'name'          => $request->name,
            'phone'         => $request->phone,
            'email'         => $request->email,
            'gender'        => $request->gender,
            'birthday'      => $request->birthday,
            'notes'         => $request->notes,
            'allergy_notes' => $request->allergy_notes,
            'hair_notes'    => $request->hair_notes,
            'is_member'     => $request->has('is_member'),
            'member_no'     => $request->member_no,
        ]);
  • $customer->update([...]):對這筆顧客資料進行更新。 api.laravel
  • 同樣使用大量指派,只更新有送出的欄位。 api.laravel
  • is_member 一樣用 $request->has('is_member') 判斷 checkbox 是否勾選。 api.laravel
  • updated_at 會由 Eloquent 自動更新為當前時間。 api.laravel

導回列表頁

        return redirect()->route('customers.index')->with('success', '顧客資料更新成功!');
    }
  • 更新完成後導向顧客列表頁,並帶入成功訊息。 api.laravel

刪除資料:destroy()

    public function destroy(Customer $customer)
    {
        $customer->delete();

        return redirect()->route('customers.index')->with('success', '顧客已刪除!');
    }
}
  • destroy():刪除指定的顧客資料。 api.laravel
  • $customer->delete():呼叫 Eloquent 的 delete() 方法,從資料庫刪除這筆資料。 api.laravel
  • 刪除後同樣導向列表頁,並顯示「顧客已刪除」的成功訊息。 api.laravel

剩下的畫面就是下次再來解說啦~


上一篇
美髮預約系統_customers 起手式
系列文
Laravel 讀碼源30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言