โพย Laravel CRUD

ป้าย ✍️ จด = จดเข้าห้องสอบ · ป้าย 👀 อ่าน = อ่านให้เข้าใจ ไม่ต้องจด
ถ้าเวลาไม่พอ จดข้อ 2, 6, 7, 11 ก่อน

0. Laravel คืออะไร

👀 อ่านPHP หน้าเดียว = เปิดร้านคนเดียวทำทุกอย่าง · Laravel = ร้านที่แบ่งพนักงานตามหน้าที่

พนักงานไฟล์หน้าที่
🧍 ต้อนรับRouteURL ไหน ไปหา function ไหน
👨‍🍳 พ่อครัวControllerlogic ทั้งหมด: ดึง/บันทึก/คำนวณ
🧊 ตู้เย็นModelตัวแทนตารางใน DB
🍽️ จัดจานViewHTML แสดงผล
URL → Route → Controller ⇄ Model (DB) → View

✍️ จดหั่น PHP หน้าเดียวไปไว้ไหน

ใน PHP หน้าเดียวย้ายไป
mysqli_connect(...).env
SQL, $_POST, header(), คำนวณController
HTML, <?= ?>, loop แสดงผลView
ชื่อไฟล์ index.phpRoute
ไฟล์ PHP เดิมfunction ใน ControllerView
index.phpindex()index.blade.php
create.php ส่วนแสดง formcreate()create.blade.php
create.php ส่วน if POSTstore()ไม่มี (redirect)
edit.php ส่วนแสดง formedit($id)edit.blade.php
edit.php ส่วน if POSTupdate($id)ไม่มี (redirect)
delete.phpdestroy($id)ไม่มี (redirect)

1. Blade = PHP แบบย่อ

✍️ จดใช้ในไฟล์ .blade.php เท่านั้น · {{ }} = แสดงค่า · @ = คำสั่ง

PHPBlade
<?= $x ?>{{ $x }}
$row['name']$p->name
<?php if($x){ ?> … <?php } else { ?> … <?php } ?>@if($x) … @else … @endif
<?php foreach($a as $p){ ?> … <?php } ?>@foreach($a as $p) … @endforeach
include 'x.php'@include('x')

ของ Laravel ล้วน: @csrf (ทุก form POST ต้องมี) · @method('PUT') แก้ · @method('DELETE') ลบ · @error('name') {{ $message }} @enderror

ติดจริงๆ ใช้ <?php ?> แบบเดิมใน Blade ได้

2. สูตรทำทุกหน้า

✍️ จดครั้งเดียว: .env → Model · ทุกหน้า: Route → Controller → View

// ① Route  (routes/web.php)
Route::get('/products', [ProductController::class, 'index']);
//          URL            ไฟล์ Controller          ชื่อ function

// ② Controller
public function index() {
    $products = Product::all();
    return view('products', compact('products'));
//              ชื่อไฟล์ view    ส่งตัวแปร $products
}

// ③ View  (resources/views/products.blade.php)
@foreach($products as $p)  {{ $p->name }}  @endforeach
ชื่อต้องตรงกัน 3 คู่ (พังเมื่อไหร่ เช็กตรงนี้ก่อน)
  1. Route 'index' = Controller function index()
  2. Controller view('products') = ไฟล์ products.blade.php
  3. Controller compact('products') = View $products

หน้าที่มี form ใช้ 2 route: GET เปิด form + POST รับข้อมูล

Route::get('/products/create', [ProductController::class, 'create']);
Route::post('/products',       [ProductController::class, 'store']);

3. Setup

✍️ จด.env (ชั้นนอกสุดของโปรเจกต์)

DB_DATABASE=ชื่อdb
DB_USERNAME=root
DB_PASSWORD=
php artisan make:model Product -m        # Model + ไฟล์สร้างตาราง
php artisan make:controller ProductController --resource
php artisan migrate                      # สร้างตารางจริง
php artisan route:list                   # ดู route ทั้งหมด
ได้ไฟล์ .sql มา: สร้าง DB ใน phpMyAdmin → Import · ไม่ต้อง -m ไม่ต้อง migrate

👀 อ่านRoute routes/web.php · Controller app/Http/Controllers/ · Model app/Models/ · View resources/views/ · ตาราง database/migrations/

✍️ จดMigration (ใน up())

$table->id();
$table->string('name');
$table->integer('price');
$table->foreignId('category_id')->constrained();   // ถ้ามี 2 ตาราง
$table->timestamps();

4. Model

✍️ จด

protected $fillable = ['name', 'price'];   // คอลัมน์ที่บันทึกได้
// ใช้ตารางที่ import มา:
protected $table = 'ชื่อตารางจริง';
protected $primaryKey = 'ชื่อคอลัมน์id';
public $timestamps = false;

5. Route

✍️ จดบรรทัดเดียวได้ครบทุก CRUD

use App\Http\Controllers\ProductController;
Route::resource('products', ProductController::class);
ชื่อ routemethodfunctionทำอะไร
products.indexGETindex()แสดงรายการ
products.createGETcreate()หน้า form เพิ่ม
products.storePOSTstore()บันทึกที่เพิ่ม
products.editGETedit($id)หน้า form แก้
products.updatePUTupdate($id)บันทึกที่แก้
products.destroyDELETEdestroy($id)ลบ

6. Controller (CRUD ครบ)

✍️ จดใส่ use App\Models\Product; ด้านบน

public function index() {
    $products = Product::all();
    return view('products.index', compact('products'));
}
public function create() {
    return view('products.create');
}
public function store(Request $r) {
    Product::create($r->all());
    return redirect()->route('products.index')->with('ok', 'เพิ่มแล้ว');
}
public function edit($id) {
    $product = Product::findOrFail($id);
    return view('products.edit', compact('product'));
}
public function update(Request $r, $id) {
    Product::findOrFail($id)->update($r->all());
    return redirect()->route('products.index')->with('ok', 'แก้แล้ว');
}
public function destroy($id) {
    Product::findOrFail($id)->delete();
    return redirect()->route('products.index')->with('ok', 'ลบแล้ว');
}

👀 อ่าน'products.index' = ไฟล์ resources/views/products/index.blade.php · ->with('ok',…) = ฝากข้อความให้หน้าถัดไป

7. View 3 ไฟล์

✍️ จดproducts/index.blade.php (รายการ + ปุ่มแก้ + ปุ่มลบ)

@if(session('ok')) <p>{{ session('ok') }}</p> @endif
<a href="{{ route('products.create') }}">เพิ่ม</a>
<table>
@foreach($products as $p)
<tr>
  <td>{{ $p->name }}</td>
  <td>{{ $p->price }}</td>
  <td>
    <a href="{{ route('products.edit', $p->id) }}">แก้</a>
    <form action="{{ route('products.destroy', $p->id) }}" method="POST">
      @csrf
      @method('DELETE')
      <button onclick="return confirm('ลบ?')">ลบ</button>
    </form>
  </td>
</tr>
@endforeach
</table>

✍️ จดproducts/create.blade.php

<form action="{{ route('products.store') }}" method="POST">
  @csrf
  <input name="name">
  <input name="price">
  <button>บันทึก</button>
</form>

✍️ จดproducts/edit.blade.php

<form action="{{ route('products.update', $product->id) }}" method="POST">
  @csrf
  @method('PUT')
  <input name="name" value="{{ $product->name }}">
  <input name="price" value="{{ $product->price }}">
  <button>บันทึก</button>
</form>
กฎของ form ✍️ จด
  1. method="POST" + @csrf ทุก form (ยกเว้น form ค้นหาที่ใช้ GET)
  2. แก้ = @method('PUT') · ลบ = @method('DELETE')
  3. แก้/ลบ ต้องส่ง id: route('...', $p->id)
  4. name="..." ต้องตรงกับชื่อคอลัมน์
  5. <form> ในตาราง ต้องอยู่ข้างใน <td>
  6. ลบห้ามใช้ <a href> ต้องเป็น form

8. Logic ตามโจทย์

👀 อ่านดึงข้อมูล/คำนวณ → เขียนใน Controller · แสดงตามเงื่อนไข → @if ใน View · คิดเป็น SQL ก่อน แล้วแปลงตามตาราง

✍️ จด

SQLLaravel
SELECT *Product::all()
WHERE id=5Product::find(5)
WHERE price>100Product::where('price','>',100)->get()
AND->where(...)->where(...)
BETWEEN 100 AND 500->whereBetween('price',[100,500])
LIKE '%a%'->where('name','like','%a%')
ORDER BY price DESC->orderBy('price','desc')
LIMIT 5->take(5)
COUNT/SUM/AVG/MAX/MINProduct::count() · ::sum('price') · ::avg · ::max · ::min

ลงท้าย ->get() ทุกครั้งที่ต่อ where/orderBy/take (ยกเว้น count/sum/find)

ค้นหา

public function index(Request $r) {
    $products = Product::where('name', 'like', '%'.$r->search.'%')->get();
    return view('products.index', compact('products'));
}
<form action="{{ route('products.index') }}" method="GET">
  <input name="search"> <button>ค้นหา</button>
</form>

ส่งหลายค่าไป View

$count = Product::count();
$sum = Product::sum('price');
return view('products.index', compact('products', 'count', 'sum'));
// View: ทั้งหมด {{ $count }} ชิ้น รวม {{ number_format($sum, 2) }} บาท

คำนวณ

// ใน View ทีละแถว
{{ $p->price * $p->qty }}      VAT {{ $p->price * 0.07 }}
// ใน Controller ก่อนบันทึก
$data = $r->all();
$data['total'] = $r->price * $r->qty;
Product::create($data);

แสดงตามเงื่อนไข

@if($p->price > 1000) แพง @elseif($p->price > 500) กลาง @else ถูก @endif
@if($products->isEmpty()) ไม่มีข้อมูล @endif

ตรวจข้อมูลก่อนบันทึก

$r->validate(['name' => 'required', 'price' => 'required|numeric|min:0']);
// View: @error('name') {{ $message }} @enderror

9. สองตาราง (Relationship)

👀 อ่านเช่น สินค้าอยู่ใน 1 หมวด → ตาราง products มีคอลัมน์ category_id

✍️ จด

// Model Product
public function category() { return $this->belongsTo(Category::class); }
// Model Category
public function products() { return $this->hasMany(Product::class); }

// Controller
Product::with('category')->get();          // View: {{ $p->category->name }}
Category::withCount('products')->get();    // View: {{ $c->products_count }}

✍️ จดDropdown เลือกหมวด (หน้าเพิ่ม/แก้)

// Controller create()
$categories = Category::all();
return view('products.create', compact('categories'));

// View
<select name="category_id">
@foreach($categories as $c)
  <option value="{{ $c->id }}">{{ $c->name }}</option>
@endforeach
</select>

10. ถ้าโจทย์เป็น PHP ธรรมดา

✍️ จด

// db.php  (ทุกไฟล์ใส่ include 'db.php'; บรรทัดแรก)
$c = mysqli_connect("localhost", "root", "", "ชื่อdb");

// index.php
$r = mysqli_query($c, "SELECT * FROM products");
while ($row = mysqli_fetch_assoc($r)) {
  echo $row['name'];
  echo "<a href='edit.php?id={$row['id']}'>แก้</a>";
  echo "<a href='delete.php?id={$row['id']}'>ลบ</a>";
}

// create.php (form method="POST")
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  mysqli_query($c, "INSERT INTO products(name,price) VALUES('$_POST[name]','$_POST[price]')");
  header("Location: index.php"); exit;
}

// edit.php (ใน form ใส่ <input type="hidden" name="id" value="<?= $row['id'] ?>">)
mysqli_query($c, "UPDATE products SET name='$_POST[name]', price='$_POST[price]' WHERE id=$_POST[id]");

// delete.php
mysqli_query($c, "DELETE FROM products WHERE id=$_GET[id]");
header("Location: index.php");

11. เจอ Error

✍️ จด

ขึ้นว่าแก้
419 Page Expiredใส่ @csrf
405 Method Not Allowedใส่ @method('PUT')/('DELETE')
Missing required parameterส่ง $p->id ใน route()
MassAssignmentExceptionใส่ $fillable
Unknown column 'updated_at'public $timestamps = false;
Table … doesn't existใส่ $table หรือรัน migrate
Class "Product" not founduse App\Models\Product;
View […] not foundเช็กชื่อไฟล์/โฟลเดอร์ view
Undefined variable $productsลืม compact('products') หรือชื่อไม่ตรง
Call to undefined methodชื่อ function ใน Route ไม่ตรงกับ Controller
Access denied / SQLSTATEเช็ก .env
กดแล้วข้อมูลไม่ไปdd($r->all()); บรรทัดแรกของ store/update

12. ทฤษฎี (Lec)

✍️ จด

13. ลำดับทำในห้องสอบ

✍️ จด

  1. แก้ .env → import .sql หรือ migrate
  2. สร้าง Model + $fillable (+ $table, $timestamps ถ้า import)
  3. ใส่ Route::resource
  4. ทำ index ให้ข้อมูลขึ้นก่อน (เปิด /products)
  5. เพิ่ม → ลบ → แก้ → logic พิเศษตามโจทย์ ทำทีละอัน ทดสอบทันที
  6. ติดเกิน 5 นาที ข้ามไปก่อน · พังให้เช็ก "ชื่อตรงกัน 3 คู่" กับตาราง Error