โพย Laravel CRUD
ป้าย ✍️ จด = จดเข้าห้องสอบ · ป้าย 👀 อ่าน = อ่านให้เข้าใจ ไม่ต้องจด
ถ้าเวลาไม่พอ จดข้อ 2, 6, 7, 11 ก่อน
0. Laravel คืออะไร
👀 อ่านPHP หน้าเดียว = เปิดร้านคนเดียวทำทุกอย่าง · Laravel = ร้านที่แบ่งพนักงานตามหน้าที่
| พนักงาน | ไฟล์ | หน้าที่ |
|---|---|---|
| 🧍 ต้อนรับ | Route | URL ไหน ไปหา function ไหน |
| 👨🍳 พ่อครัว | Controller | logic ทั้งหมด: ดึง/บันทึก/คำนวณ |
| 🧊 ตู้เย็น | Model | ตัวแทนตารางใน DB |
| 🍽️ จัดจาน | View | HTML แสดงผล |
✍️ จดหั่น PHP หน้าเดียวไปไว้ไหน
| ใน PHP หน้าเดียว | ย้ายไป |
|---|---|
mysqli_connect(...) | .env |
SQL, $_POST, header(), คำนวณ | Controller |
HTML, <?= ?>, loop แสดงผล | View |
ชื่อไฟล์ index.php | Route |
| ไฟล์ PHP เดิม | function ใน Controller | View |
|---|---|---|
index.php | index() | index.blade.php |
create.php ส่วนแสดง form | create() | create.blade.php |
create.php ส่วน if POST | store() | ไม่มี (redirect) |
edit.php ส่วนแสดง form | edit($id) | edit.blade.php |
edit.php ส่วน if POST | update($id) | ไม่มี (redirect) |
delete.php | destroy($id) | ไม่มี (redirect) |
1. Blade = PHP แบบย่อ
✍️ จดใช้ในไฟล์ .blade.php เท่านั้น · {{ }} = แสดงค่า · @ = คำสั่ง
| PHP | Blade |
|---|---|
<?= $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
- Route
'index'= Controllerfunction index() - Controller
view('products')= ไฟล์products.blade.php - 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 ทั้งหมด
-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);
| ชื่อ route | method | function | ทำอะไร |
|---|---|---|---|
| products.index | GET | index() | แสดงรายการ |
| products.create | GET | create() | หน้า form เพิ่ม |
| products.store | POST | store() | บันทึกที่เพิ่ม |
| products.edit | GET | edit($id) | หน้า form แก้ |
| products.update | PUT | update($id) | บันทึกที่แก้ |
| products.destroy | DELETE | destroy($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>
method="POST"+@csrfทุก form (ยกเว้น form ค้นหาที่ใช้ GET)- แก้ =
@method('PUT')· ลบ =@method('DELETE') - แก้/ลบ ต้องส่ง id:
route('...', $p->id) name="..."ต้องตรงกับชื่อคอลัมน์<form>ในตาราง ต้องอยู่ข้างใน<td>- ลบห้ามใช้
<a href>ต้องเป็น form
8. Logic ตามโจทย์
👀 อ่านดึงข้อมูล/คำนวณ → เขียนใน Controller · แสดงตามเงื่อนไข → @if ใน View · คิดเป็น SQL ก่อน แล้วแปลงตามตาราง
✍️ จด
| SQL | Laravel |
|---|---|
SELECT * | Product::all() |
WHERE id=5 | Product::find(5) |
WHERE price>100 | Product::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/MIN | Product::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 found | use 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)
✍️ จด
- MVC = Model (DB) · View (หน้าจอ) · Controller (ตัวกลางสั่งงาน)
- GET ส่งทาง URL ใช้ดึง · POST ส่งใน body ใช้บันทึก
- CSRF = ปลอม form จากเว็บอื่น ป้องกันด้วย
@csrf - ORM/Eloquent = จัดการ DB ด้วย object แทนเขียน SQL
- Migration = โครงสร้างตารางที่เขียนเป็นโค้ด
- Blade = template ของ Laravel ·
{{ }}กัน XSS - Session = เก็บข้อมูลข้ามหน้า เช่น ข้อความ "ลบแล้ว"
- SQL Injection = ป้องกันด้วย Eloquent / prepared statement
- CSS selector
.class#idtag· box model margin › border › padding › content - Bootstrap grid 12 ช่อง
container›row›col-md-6·btn btn-primary·table·form-control·alert alert-success
13. ลำดับทำในห้องสอบ
✍️ จด
- แก้
.env→ import .sql หรือ migrate - สร้าง Model +
$fillable(+$table,$timestampsถ้า import) - ใส่
Route::resource - ทำ index ให้ข้อมูลขึ้นก่อน (เปิด
/products) - เพิ่ม → ลบ → แก้ → logic พิเศษตามโจทย์ ทำทีละอัน ทดสอบทันที
- ติดเกิน 5 นาที ข้ามไปก่อน · พังให้เช็ก "ชื่อตรงกัน 3 คู่" กับตาราง Error