Laravel Beginner Guide

Laravel for beginnersLaravel สำหรับผู้เริ่มต้น

Laravel Beginner Guide

Build a task list, one request at a timeสร้างแอปรายการงาน ทีละคำขอ

Laravel is the most popular PHP framework. In this guide you build one small app, a task list where you can add, tick off and delete tasks. Along the way you learn the parts every Laravel app is made of: routes, migrations, models, controllers, Blade views, validation and tests. Laravel คือเฟรมเวิร์ก PHP ที่ได้รับความนิยมมากที่สุด ในคู่มือนี้คุณจะสร้างแอปเล็กๆ หนึ่งตัว คือรายการงานที่เพิ่ม ติ๊กว่าเสร็จ และลบงานได้ ระหว่างทางจะได้เรียนส่วนประกอบที่ทุกแอป Laravel มี ได้แก่ route, migration, โมเดล, controller, view ของ Blade, validation และเทสต์

Every command on this page was run for real, and the output under it is what came back. The app was saved after each lesson, so the files you see are exactly the files that produced that output. You'll need some PHP first: the PHP Beginner Guide covers everything used here. ทุกคำสั่งในหน้านี้ถูกรันจริง และผลลัพธ์ด้านล่างคือสิ่งที่ได้กลับมา แอปถูกบันทึกไว้หลังจบแต่ละบท ไฟล์ที่เห็นจึงเป็นไฟล์เดียวกับที่ให้ผลลัพธ์นั้น ควรรู้ PHP มาก่อน โดย PHP Beginner Guide ครอบคลุมทุกอย่างที่ใช้ในหน้านี้

  • Frameworkเฟรมเวิร์ก Laravel 13.34.0
  • Run onรันบน PHP 8.3.33
  • Testsเทสต์ 10 passingผ่าน 10 ตัว

Lesson 1 · The big pictureบทที่ 1 · ภาพรวม

How Laravel worksLaravel ทำงานอย่างไร

In plain PHP, every page is its own file that does everything: read input, query the database, print HTML. Laravel splits that work into small pieces that each live in a known folder, and connects them for you. Learn where each piece goes and you can find your way around any Laravel project. ใน PHP ล้วน แต่ละหน้าคือไฟล์หนึ่งไฟล์ที่ทำทุกอย่างเอง ทั้งอ่านข้อมูล คิวรีฐานข้อมูล และพิมพ์ HTML Laravel แบ่งงานเหล่านั้นเป็นชิ้นเล็กๆ ที่แต่ละชิ้นอยู่ในโฟลเดอร์ที่กำหนดไว้ แล้วเชื่อมทุกชิ้นให้เรา ถ้ารู้ว่าแต่ละชิ้นอยู่ที่ไหน ก็จะเข้าใจโปรเจกต์ Laravel ได้ทุกโปรเจกต์

A request travels from the browser through public/index.php, middleware and the router to a controller, which uses a model and a view to build the response. Browser public/ index.php Middleware Router routes/web.php Controller app/Http/Controllers Model app/Models Database View (Blade) resources/views HTML response
Every request takes the same path. You write the highlighted parts; Laravel provides the rest.ทุกคำขอเดินทางผ่านเส้นทางเดียวกัน เราเขียนเฉพาะส่วนที่ไฮไลต์ไว้ ส่วนที่เหลือ Laravel จัดการให้
Pieceส่วนประกอบLives inอยู่ที่Jobหน้าที่
RouteRouteroutes/web.phpmatches a method and URL to codeจับคู่ method และ URL กับโค้ด
ControllerControllerapp/Http/Controllershandles the request: reads input, uses models, returns a pageจัดการคำขอ: อ่านข้อมูล ใช้โมเดล แล้วคืนหน้าเว็บ
Modelโมเดลapp/Modelsone class per database tableหนึ่งคลาสต่อหนึ่งตารางในฐานข้อมูล
MigrationMigrationdatabase/migrationscreates and changes tablesสร้างและแก้ไขตาราง
ViewViewresources/viewsHTML templates written in Bladeเทมเพลต HTML ที่เขียนด้วย Blade
Testเทสต์tests/Featuresends fake requests and checks the resultsส่งคำขอจำลองแล้วตรวจผล
You needสิ่งที่ต้องมี

PHP 8.3 or newer and Composer. On Windows and macOS, Laravel Herd installs both in one go. Node.js is only needed later, for building CSS and JavaScript with Vite; this guide doesn't use it.PHP 8.3 ขึ้นไป และ Composer บน Windows และ macOS ติดตั้งทั้งสองอย่างได้ในครั้งเดียวด้วย Laravel Herd ส่วน Node.js ต้องใช้ภายหลังสำหรับ build CSS และ JavaScript ด้วย Vite ซึ่งคู่มือนี้ไม่ได้ใช้

Lesson 2 · Setupบทที่ 2 · ติดตั้ง

Install & first runติดตั้งและรันครั้งแรก

Composer downloads Laravel and everything it depends on, creates a .env settings file, makes an SQLite database and runs the first migrations, all in one command. Composer ดาวน์โหลด Laravel และทุกอย่างที่มันต้องใช้ สร้างไฟล์ตั้งค่า .env สร้างฐานข้อมูล SQLite และรัน migration ชุดแรก ทั้งหมดในคำสั่งเดียว

Create the projectสร้างโปรเจกต์Laravel 13.34.0 · PHP 8.3.33
$ composer create-project laravel/laravel taskapp
Creating a "laravel/laravel" project at "./taskapp"
Installing laravel/laravel (v13.10.1)
  - Installing laravel/laravel (v13.10.1): Extracting archive
Created project in ~/taskapp
⋯ 246 lines of package downloads ⋯

No security vulnerability advisories found.
> @php artisan key:generate --ansi

   INFO  Application key set successfully.  

> @php -r "file_exists('database/database.sqlite') || touch('database/database.sqlite');"
> @php artisan migrate --graceful --ansi

   INFO  Preparing database.  

  Creating migration table ................................................. 5.71ms DONE

   INFO  Running migrations.  

  0001_01_01_000000_create_users_table ..................................... 1.95ms DONE
  0001_01_01_000001_create_cache_table ..................................... 1.11ms DONE
  0001_01_01_000002_create_jobs_table ...................................... 1.66ms DONE

$ cd taskapp && php artisan --version
Laravel Framework 13.34.0

The package list in the middle is collapsed. Folder paths are shortened to ~.รายการแพ็กเกจตรงกลางถูกย่อไว้ พาธโฟลเดอร์ถูกย่อเป็น ~

php artisan is Laravel's command-line tool; you'll use it in every lesson. Start the development server, then open http://127.0.0.1:8000 in your browser. php artisan คือเครื่องมือ command line ของ Laravel ที่จะได้ใช้ในทุกบท เปิดเซิร์ฟเวอร์สำหรับพัฒนา แล้วเปิด http://127.0.0.1:8000 ในเบราว์เซอร์

Terminal 1 · serverเทอร์มินัล 1 · เซิร์ฟเวอร์Laravel 13.34.0 · PHP 8.3.33
$ php artisan serve
   INFO  Server running on [http://127.0.0.1:8000].  

  Press Ctrl+C to stop the server

  2026-10-05 22:15:28 / ...................................................... ~ 0.07ms
Terminal 2 · requestเทอร์มินัล 2 · คำขอLaravel 13.34.0 · PHP 8.3.33
$ curl -s -o /dev/null -w "%{http_code} %{content_type}\n" http://127.0.0.1:8000/
200 text/html; charset=utf-8

The project has many folders, but you'll spend most of your time in a handful: โปรเจกต์มีหลายโฟลเดอร์ แต่เวลาส่วนใหญ่จะอยู่กับไม่กี่โฟลเดอร์นี้:

PathพาธWhat's insideมีอะไรอยู่
app/your PHP classes: controllers, modelsคลาส PHP ของเรา: controller และโมเดล
routes/web.phpthe list of URLs your app answersรายการ URL ที่แอปตอบกลับ
resources/views/Blade templatesเทมเพลต Blade
database/migrations, factories, and the SQLite filemigration, factory และไฟล์ SQLite
tests/your testsเทสต์ของเรา
public/the web root: index.php, images, built CSSโฟลเดอร์หลักของเว็บ: index.php รูปภาพ และ CSS ที่ build แล้ว
.envsettings and secrets for this machineการตั้งค่าและความลับของเครื่องนี้
vendor/Laravel itself and other packages; never editตัว Laravel และแพ็กเกจอื่นๆ ห้ามแก้ไข

These lines of the new .env matter most. A fresh Laravel 13 app uses SQLite, so there is no database server to set up. บรรทัดเหล่านี้ใน .env ที่สร้างใหม่สำคัญที่สุด แอป Laravel 13 ใหม่ใช้ SQLite จึงไม่ต้องตั้งค่าเซิร์ฟเวอร์ฐานข้อมูลเลย

Keep secretเก็บเป็นความลับ

.env also holds APP_KEY and database passwords. It's in .gitignore already; never commit it. On a live server set APP_DEBUG=false, or error pages will show your code and settings to visitors..env ยังเก็บ APP_KEY และรหัสผ่านฐานข้อมูลด้วย ไฟล์นี้อยู่ใน .gitignore แล้ว ห้าม commit เด็ดขาด บนเซิร์ฟเวอร์จริงให้ตั้ง APP_DEBUG=false ไม่อย่างนั้นหน้า error จะเปิดเผยโค้ดและการตั้งค่าให้ผู้เข้าชมเห็น

Lesson 3 · Requestsบทที่ 3 · คำขอ

RoutesRoute

A route says: "when a request with this method and this URL arrives, run this code". Add two routes to routes/web.php: route บอกว่า "เมื่อมีคำขอที่ใช้ method นี้และ URL นี้เข้ามา ให้รันโค้ดนี้" เพิ่มสอง route ใน routes/web.php:

  • {name} is a route parameter. It matches one part of the URL, and Laravel passes it to the function as $name.{name} คือพารามิเตอร์ของ route ซึ่งตรงกับ URL หนึ่งช่วง และ Laravel ส่งค่านั้นให้ฟังก์ชันเป็น $name
  • Return a string and Laravel sends it as HTML. Return an array and it sends JSON.ถ้าคืนค่าเป็นข้อความ Laravel จะส่งเป็น HTML ถ้าคืนเป็นอาร์เรย์จะส่งเป็น JSON
Terminalเทอร์มินัลLaravel 13.34.0 · PHP 8.3.33
$ curl -i http://127.0.0.1:8000/hello/Mya
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache, private

Hello, Mya!

$ curl -i http://127.0.0.1:8000/about
HTTP/1.1 200 OK
Cache-Control: no-cache, private
Content-Type: application/json

{"app":"Task list","version":1}

$ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/hello
404

$ php artisan route:list --except-vendor
  GET|HEAD       / .................................................... routes/web.php:5
  GET|HEAD       about ............................................... routes/web.php:13
  GET|HEAD       hello/{name} ......................................... routes/web.php:9

                                                                      Showing [3] routes

Cookie, date and server headers are left out of the -i output. /hello without a name gives 404: the {name} part is required. route:list shows every route and the file and line that defines it.ส่วนหัวคุกกี้ วันที่ และเซิร์ฟเวอร์ ถูกตัดออกจากผลลัพธ์ของ -i ส่วน /hello ที่ไม่มีชื่อได้ 404 เพราะ {name} เป็นส่วนที่ต้องมี route:list แสดง route ทั้งหมดพร้อมไฟล์และบรรทัดที่ประกาศ

RouteRouteUsed forใช้สำหรับ
Route::get('/tasks', …)show a pageแสดงหน้าเว็บ
Route::post('/tasks', …)receive a form and create somethingรับฟอร์มแล้วสร้างข้อมูล
Route::patch('/tasks/{task}', …)change somethingแก้ไขข้อมูล
Route::delete('/tasks/{task}', …)remove somethingลบข้อมูล
->name('tasks.index')a name to link to with route('tasks.index')ตั้งชื่อเพื่อลิงก์ด้วย route('tasks.index')
{name?}an optional parameter (give $name a default)พารามิเตอร์ที่ไม่บังคับ (ต้องกำหนดค่าเริ่มต้นให้ $name)
EscapeEscape

The /hello/{name} route prints $name without escaping. That's fine for a first test, but for anything a visitor controls, return a Blade view (lesson 6) or wrap it in e($name).route /hello/{name} พิมพ์ $name โดยไม่ escape ซึ่งใช้ได้สำหรับการลองครั้งแรก แต่ถ้าเป็นค่าที่ผู้เข้าชมกำหนดเอง ให้คืน view ของ Blade (บทที่ 6) หรือครอบด้วย e($name)

Lesson 4 · Dataบทที่ 4 · ข้อมูล

Migrations & modelsMigration และโมเดล

One artisan command creates the three files a new kind of data needs: the model (-m adds a migration, -f a factory). คำสั่ง artisan เดียวสร้างสามไฟล์ที่ข้อมูลชนิดใหม่ต้องมี คือโมเดล (-m เพิ่ม migration และ -f เพิ่ม factory)

Terminalเทอร์มินัลLaravel 13.34.0 · PHP 8.3.33
$ php artisan make:model Task -mf
   INFO  Model [app/Models/Task.php] created successfully.  

   INFO  Factory [database/factories/TaskFactory.php] created successfully.  

   INFO  Migration [database/migrations/2026_10_05_151537_create_tasks_table.php] created successfully.  

A migration describes a table in PHP, so everyone on the team can build the same database with one command. Add the two columns a task needs: migration อธิบายตารางด้วยโค้ด PHP ทุกคนในทีมจึงสร้างฐานข้อมูลแบบเดียวกันได้ด้วยคำสั่งเดียว เพิ่มสองคอลัมน์ที่งานแต่ละงานต้องมี:

Terminalเทอร์มินัลLaravel 13.34.0 · PHP 8.3.33
$ php artisan migrate
   INFO  Running migrations.  

  2026_10_05_151537_create_tasks_table ..................................... 1.84ms DONE

The model is the PHP class for that table. Laravel connects them by name: the Task model uses the tasks table automatically. โมเดล คือคลาส PHP ของตารางนั้น Laravel เชื่อมทั้งสองด้วยชื่อ โมเดล Task จะใช้ตาราง tasks โดยอัตโนมัติ

  • #[Fillable([...])] lists the columns that Task::create([...]) may fill. Anything else in the array is quietly ignored (see lesson 11).#[Fillable([...])] ระบุคอลัมน์ที่ Task::create([...]) ใส่ค่าได้ ค่าอื่นในอาร์เรย์จะถูกละเว้นโดยไม่แจ้งเตือน (ดูบทที่ 11)
  • casts() turns the database's 0/1 into a real true/false.casts() แปลง 0/1 จากฐานข้อมูลให้เป็น true/false จริงๆ

The factory describes a fake task. Tests use it to create data in one line (lesson 8). factory อธิบายงานปลอมหนึ่งงาน เทสต์ใช้มันสร้างข้อมูลได้ในบรรทัดเดียว (บทที่ 8)

Migration codeโค้ดใน migrationColumnคอลัมน์
$table->id()auto-numbered primary key idprimary key id ที่เพิ่มหมายเลขอัตโนมัติ
$table->string('title')short text, required by defaultข้อความสั้น ต้องมีค่าเป็นค่าเริ่มต้น
->nullable()allow the column to be emptyอนุญาตให้คอลัมน์ว่างได้
$table->boolean('done')->default(false)true/false with a defaultจริง/เท็จ พร้อมค่าเริ่มต้น
$table->timestamps()created_at and updated_at, filled for youcreated_at และ updated_at ที่ระบบเติมให้

Lesson 5 · Dataบทที่ 5 · ข้อมูล

Eloquent with TinkerEloquent ด้วย Tinker

Eloquent is Laravel's way of working with the database through models: you call methods, and it writes the SQL. Tinker (php artisan tinker) lets you try any of it against your real local database. Here it runs with --execute, so the exact code and output can be shown. Eloquent คือวิธีทำงานกับฐานข้อมูลผ่านโมเดลของ Laravel เราเรียก method แล้วมันเขียน SQL ให้ ส่วน Tinker (php artisan tinker) ให้เราลองโค้ดเหล่านี้กับฐานข้อมูลจริงในเครื่องได้ ในที่นี้รันด้วย --execute เพื่อแสดงโค้ดและผลลัพธ์ได้ตรงตัว

Create one taskสร้างงานหนึ่งงานLaravel 13.34.0 · PHP 8.3.33
$ php artisan tinker --execute="\$task = App\Models\Task::create(['title' => 'Learn routes']); echo \$task->toJson(JSON_PRETTY_PRINT);"
{
    "title": "Learn routes",
    "updated_at": "2026-10-05T15:15:45.000000Z",
    "created_at": "2026-10-05T15:15:45.000000Z",
    "id": 1
}

done is missing because the database filled in its default; the object in memory never saw it. $task->fresh() would reload the row.done ไม่อยู่ใน JSON เพราะฐานข้อมูลเป็นคนใส่ค่าเริ่มต้น ออบเจกต์ในหน่วยความจำจึงไม่รู้ $task->fresh() จะโหลดแถวนั้นใหม่

QueryคิวรีLaravel 13.34.0 · PHP 8.3.33
$ php artisan tinker --execute="$(cat tinker-query.php)"
3 tasks
2 not done
["Write a migration","Try Tinker"]
EloquentEloquentDoesทำอะไร
Task::create([...])insert a row and return the modelเพิ่มแถวแล้วคืนโมเดล
Task::find(1)the row with id 1, or nullแถวที่มี id 1 หรือ null
Task::findOrFail(1)the row, or a 404 pageแถวนั้น หรือหน้า 404
Task::where('done', false)->get()all matching rows, as a Collectionทุกแถวที่ตรง ในรูป Collection
Task::orderBy('id')->first()the first row in that orderแถวแรกตามลำดับนั้น
Task::count()how many rowsจำนวนแถว
$task->update([...])change and save columnsแก้ไขและบันทึกคอลัมน์
$task->delete()remove the rowลบแถว
Tipเคล็ดลับ

Nothing reaches the database until a method like get(), first() or count() runs. Until then where() and orderBy() just build the query, so you can chain as many as you need.จะยังไม่มีอะไรถูกส่งไปยังฐานข้อมูลจนกว่าจะเรียก method อย่าง get(), first() หรือ count() ก่อนหน้านั้น where() และ orderBy() แค่สร้างคิวรี จึงต่อกันได้มากเท่าที่ต้องการ

Lesson 6 · Pagesบทที่ 6 · หน้าเว็บ

Controllers & BladeController และ Blade

Closures in routes/web.php get messy quickly. A controller groups the code for one kind of thing in a class, one method per action, and each route points at a method. closure ใน routes/web.php จะรกได้เร็ว controller รวมโค้ดของเรื่องหนึ่งไว้ในคลาสเดียว หนึ่ง method ต่อหนึ่งการกระทำ และแต่ละ route ชี้ไปที่ method หนึ่งตัว

Terminalเทอร์มินัลLaravel 13.34.0 · PHP 8.3.33
$ php artisan make:controller TaskController
   INFO  Controller [app/Http/Controllers/TaskController.php] created successfully.  
  • index() loads the tasks, unfinished first, and hands them to the view as $tasks.index() โหลดงานทั้งหมดโดยให้งานที่ยังไม่เสร็จขึ้นก่อน แล้วส่งให้ view เป็น $tasks
  • store() creates a task from the form, then redirects. Never return a page straight after a POST: refreshing would submit the form twice.store() สร้างงานจากฟอร์ม แล้ว redirect ห้ามคืนหน้าเว็บทันทีหลัง POST เพราะการรีเฟรชจะส่งฟอร์มซ้ำ
  • update(Task $task) uses route model binding: because the parameter is typed Task and named like {task}, Laravel loads task 2 for /tasks/2, or answers 404 if it doesn't exist.update(Task $task) ใช้ route model binding: เพราะพารามิเตอร์ระบุชนิดเป็น Task และชื่อตรงกับ {task} Laravel จึงโหลดงานที่ 2 ให้เมื่อเรียก /tasks/2 หรือตอบ 404 ถ้าไม่มีงานนั้น

The view is a Blade template: HTML with a few extras. view('tasks.index') means resources/views/tasks/index.blade.php. view คือเทมเพลต Blade ซึ่งก็คือ HTML ที่มีความสามารถเพิ่มเล็กน้อย view('tasks.index') หมายถึง resources/views/tasks/index.blade.php

BladeBladeDoesทำอะไร
{{ $x }}print a value, escaped for HTMLพิมพ์ค่า โดย escape HTML ให้
@foreach … @endforeacha loopลูป
@forelse … @empty … @endforelsea loop with a "nothing here" partลูปที่มีส่วน "ไม่มีข้อมูล"
@if … @endifa conditionเงื่อนไข
@csrfthe hidden security token every form needstoken ความปลอดภัยที่ซ่อนไว้ ซึ่งทุกฟอร์มต้องมี
@method('PATCH')send PATCH or DELETE from an HTML formส่ง PATCH หรือ DELETE จากฟอร์ม HTML

Now real requests against the running app. jar.txt keeps the session cookie between requests, and $TOKEN holds the _token value copied from the first page. ต่อไปคือคำขอจริงที่ส่งไปยังแอปที่กำลังทำงาน jar.txt เก็บคุกกี้ session ระหว่างคำขอ และ $TOKEN เก็บค่า _token ที่คัดลอกจากหน้าแรก

RequestsคำขอLaravel 13.34.0 · PHP 8.3.33
$ curl -s -c jar.txt http://127.0.0.1:8000/tasks
<!doctype html>
<title>Tasks</title>
<h1>Tasks (3)</h1>

<form method="POST" action="http://127.0.0.1:8000/tasks">
    <input type="hidden" name="_token" value="Yl0fPufZEHnlurrnz4nYELvjbSdJpzCF7Lgix6mv" autocomplete="off">    <input name="title" placeholder="New task">
    <button>Add</button>
</form>

<ul>
            <li>
            <form method="POST" action="http://127.0.0.1:8000/tasks/2">
                <input type="hidden" name="_token" value="Yl0fPufZEHnlurrnz4nYELvjbSdJpzCF7Lgix6mv" autocomplete="off">                <input type="hidden" name="_method" value="PATCH">                <button>[ ]</button>
                Write a migration
            </form>
        </li>
            <li>
            <form method="POST" action="http://127.0.0.1:8000/tasks/3">
                <input type="hidden" name="_token" value="Yl0fPufZEHnlurrnz4nYELvjbSdJpzCF7Lgix6mv" autocomplete="off">                <input type="hidden" name="_method" value="PATCH">                <button>[ ]</button>
                Try Tinker
            </form>
        </li>
            <li>
            <form method="POST" action="http://127.0.0.1:8000/tasks/1">
                <input type="hidden" name="_token" value="Yl0fPufZEHnlurrnz4nYELvjbSdJpzCF7Lgix6mv" autocomplete="off">                <input type="hidden" name="_method" value="PATCH">                <button>[x]</button>
                Learn routes
            </form>
        </li>
    </ul>

$ curl -si -b jar.txt -c jar.txt -X POST -d "_token=$TOKEN" -d "title=Style the page" http://127.0.0.1:8000/tasks | grep -i "^HTTP\|^location"
HTTP/1.1 302 Found
Location: http://127.0.0.1:8000/tasks

$ curl -si -b jar.txt -c jar.txt -X POST -d "_token=$TOKEN" -d "_method=PATCH" http://127.0.0.1:8000/tasks/2 | grep -i "^HTTP\|^location"
HTTP/1.1 302 Found
Location: http://127.0.0.1:8000/tasks

$ curl -s -o /dev/null -w "%{http_code}\n" -b jar.txt -c jar.txt -X POST -d "_token=$TOKEN" -d "title=" http://127.0.0.1:8000/tasks
500

$ grep -o "SQLSTATE[^(]*" storage/logs/laravel.log | head -1
SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: tasks.title 
Bugบั๊ก

The last request sent an empty title and crashed the app with a 500 error. Laravel turns empty form fields into null, and the migration said title can't be null. The next lesson fixes this properly.คำขอสุดท้ายส่งชื่องานว่าง ทำให้แอปพังด้วยข้อผิดพลาด 500 Laravel แปลงช่องฟอร์มที่ว่างให้เป็น null แต่ migration กำหนดว่า title เป็น null ไม่ได้ บทถัดไปจะแก้ปัญหานี้อย่างถูกวิธี

Lesson 7 · Inputบทที่ 7 · ข้อมูลขาเข้า

Forms & validationฟอร์มและ validation

Never trust form input. $request->validate() checks each field against a list of rules. If any rule fails, Laravel stops right there, sends the visitor back to the form with error messages and what they typed, and the rest of your method never runs. อย่าไว้ใจข้อมูลจากฟอร์ม $request->validate() ตรวจแต่ละฟิลด์ตามรายการกฎ ถ้ามีกฎข้อใดไม่ผ่าน Laravel จะหยุดทันที ส่งผู้เข้าชมกลับไปที่ฟอร์มพร้อมข้อความผิดพลาดและสิ่งที่พิมพ์ไว้ และโค้ดที่เหลือใน method จะไม่ทำงาน

  1. Fails: Laravel saves the error messages and the old input in the session and redirects back (302).ไม่ผ่าน: Laravel เก็บข้อความผิดพลาดและข้อมูลเดิมไว้ใน session แล้ว redirect กลับ (302)
  2. @error('title') shows that field's message, and old('title') puts back what they typed.@error('title') แสดงข้อความของฟิลด์นั้น และ old('title') เติมสิ่งที่พิมพ์ไว้กลับเข้าไป
  3. Passes: $validated holds only the checked fields. The task is created, and ->with('status', …) flashes a message that's shown once.ผ่าน: $validated มีเฉพาะฟิลด์ที่ตรวจแล้ว งานถูกสร้าง และ ->with('status', …) แฟลชข้อความที่แสดงเพียงครั้งเดียว
RequestsคำขอLaravel 13.34.0 · PHP 8.3.33
$ curl -si -b jar.txt -c jar.txt -X POST -d "_token=$TOKEN" -d "title=" http://127.0.0.1:8000/tasks | grep -i "^HTTP\|^location"
HTTP/1.1 302 Found
Location: http://127.0.0.1:8000/tasks

$ curl -s -b jar.txt -c jar.txt http://127.0.0.1:8000/tasks | sed -n '1,12p'
<!doctype html>
<title>Tasks</title>
<h1>Tasks (4)</h1>


<form method="POST" action="http://127.0.0.1:8000/tasks">
    <input type="hidden" name="_token" value="syhvKtkw4XgOGtEs0t1svQonuivpjDOAWMMyP7C7" autocomplete="off">    <input name="title" value="" placeholder="New task">
    <button>Add</button>
            <p class="error">The title field is required.</p>
    </form>

<ul>

$ curl -s -o /dev/null -b jar.txt -c jar.txt -X POST -d "_token=$TOKEN" -d "title=$LONG" http://127.0.0.1:8000/tasks

$ curl -s -b jar.txt -c jar.txt http://127.0.0.1:8000/tasks | grep "error"
            <p class="error">The title field must not be greater than 100 characters.</p>

$ curl -si -b jar.txt -c jar.txt -X POST -d "_token=$TOKEN" -d "title=Add validation" http://127.0.0.1:8000/tasks | grep -i "^HTTP\|^location"
HTTP/1.1 302 Found
Location: http://127.0.0.1:8000/tasks

$ curl -s -b jar.txt -c jar.txt http://127.0.0.1:8000/tasks | sed -n '1,8p'
<!doctype html>
<title>Tasks</title>
<h1>Tasks (5)</h1>

    <p class="status">Task added.</p>

<form method="POST" action="http://127.0.0.1:8000/tasks">
    <input type="hidden" name="_token" value="syhvKtkw4XgOGtEs0t1svQonuivpjDOAWMMyP7C7" autocomplete="off">    <input name="title" value="" placeholder="New task">

$ curl -s -o /dev/null -w "%{http_code}\n" -b jar.txt -c jar.txt -X POST -d "_token=$TOKEN" --data-urlencode "title=   " http://127.0.0.1:8000/tasks
302

$ curl -s -b jar.txt -c jar.txt http://127.0.0.1:8000/tasks | grep -E "error|<h1>"
<h1>Tasks (5)</h1>
            <p class="error">The title field is required.</p>

$LONG is 101 letters. The last two requests send a title of three spaces. It fails required too, because Laravel trims every input and then turns empty strings into null, so the count stays at 5.$LONG คือตัวอักษร 101 ตัว สองคำขอสุดท้ายส่งชื่องานที่เป็นช่องว่างสามตัว ซึ่งก็ไม่ผ่าน required เช่นกัน เพราะ Laravel ตัดช่องว่างของทุกค่าที่ส่งมา แล้วแปลงข้อความว่างเป็น null จำนวนงานจึงยังเป็น 5

RuleกฎChecksตรวจอะไร
requiredpresent and not emptyต้องมีและไม่ว่าง
string integer booleanthe type of valueชนิดของค่า
max:100 min:3length for text, size for numbersความยาวของข้อความ หรือขนาดของตัวเลข
emaillooks like an email addressมีรูปแบบเป็นอีเมล
unique:tasks,titleno other row has this value (practice task 2)ไม่มีแถวอื่นที่มีค่านี้ (แบบฝึกหัดที่ 2)
nullablemay be left emptyเว้นว่างได้

Lesson 8 · Confidenceบทที่ 8 · ความมั่นใจ

Feature testsFeature test

Clicking through every page after each change gets old fast. A feature test sends fake requests to your app and checks the response and the database, with no browser and no server. Here the test for deleting a task was written before the delete feature existed. การคลิกทุกหน้าหลังแก้โค้ดทุกครั้งน่าเบื่อมาก feature test ส่งคำขอจำลองเข้าไปในแอป แล้วตรวจคำตอบและฐานข้อมูล โดยไม่ต้องมีเบราว์เซอร์หรือเซิร์ฟเวอร์ ในที่นี้เทสต์สำหรับการลบงานถูกเขียนก่อนที่ฟีเจอร์ลบจะมีอยู่จริง

Terminalเทอร์มินัลLaravel 13.34.0 · PHP 8.3.33
$ php artisan make:test TaskTest
   INFO  Test [tests/Feature/TaskTest.php] created successfully.  
In a testในเทสต์Doesทำอะไร
use RefreshDatabase;every test starts with empty tables (an in-memory SQLite database, set in phpunit.xml)ทุกเทสต์เริ่มด้วยตารางว่าง (ฐานข้อมูล SQLite ในหน่วยความจำ ตั้งค่าไว้ใน phpunit.xml)
Task::factory()->create()insert a fake taskเพิ่มงานปลอม
$this->get('/tasks')send a fake request (also post, patch, delete)ส่งคำขอจำลอง (มี post, patch, delete ด้วย)
assertOk() assertRedirect()check the status codeตรวจรหัสสถานะ
assertSee('…')check the page contains this textตรวจว่าหน้าเว็บมีข้อความนี้
assertSessionHasErrors('title')check validation failed for a fieldตรวจว่า validation ของฟิลด์นั้นไม่ผ่าน
assertDatabaseHas('tasks', [...])check a matching row existsตรวจว่ามีแถวที่ตรงอยู่

No CSRF token is needed: Laravel skips that check while tests run. Now run the tests. The last one fails, because there's no route for DELETE yet: ไม่ต้องใช้ CSRF token เพราะ Laravel ข้ามการตรวจนี้ระหว่างรันเทสต์ ทีนี้รันเทสต์ เทสต์สุดท้ายไม่ผ่าน เพราะยังไม่มี route สำหรับ DELETE:

Run 1 · before the delete featureรันครั้งที่ 1 · ก่อนมีฟีเจอร์ลบLaravel 13.34.0 · PHP 8.3.33
$ php artisan test
   PASS  Tests\Unit\ExampleTest
  ✓ that true is true

   PASS  Tests\Feature\ExampleTest
  ✓ the application returns a successful response                                  0.06s  

   FAIL  Tests\Feature\TaskTest
  ✓ the list shows tasks                                                           0.05s  
  ✓ a task can be created                                                          0.01s  
  ✓ the title is required
  ✓ a task can be marked done
  ⨯ a task can be deleted                                                          0.07s  
  ──────────────────────────────────────────────────────────────────────────────────────  
   FAILED  Tests\Feature\TaskTest > a task can be deleted                                 
  Expected response status code [201, 301, 302, 303, 307, 308] but received 405.
Failed asserting that false is true.

  at tests/Feature/TaskTest.php:54
     50▕         $task = Task::factory()->create();
     51▕ 
     52▕         $response = $this->delete("/tasks/{$task->id}");
     53▕ 
  ➜  54▕         $response->assertRedirect('/tasks');
     55▕         $this->assertModelMissing($task);
     56▕     }
     57▕ }
     58▕


  Tests:    1 failed, 6 passed (12 assertions)
  Duration: 0.35s

405 means "the URL exists, but not for this method": /tasks/{task} only answers PATCH so far.405 หมายถึง "URL นี้มีอยู่ แต่ไม่รับ method นี้" ตอนนี้ /tasks/{task} รับเฉพาะ PATCH

Add the route and the controller method: เพิ่ม route และ method ใน controller:

Run 2 · after adding destroy()รันครั้งที่ 2 · หลังเพิ่ม destroy()Laravel 13.34.0 · PHP 8.3.33
$ php artisan test
   PASS  Tests\Unit\ExampleTest
  ✓ that true is true

   PASS  Tests\Feature\ExampleTest
  ✓ the application returns a successful response                                  0.06s  

   PASS  Tests\Feature\TaskTest
  ✓ the list shows tasks                                                           0.05s  
  ✓ a task can be created                                                          0.01s  
  ✓ the title is required                                                          0.01s  
  ✓ a task can be marked done
  ✓ a task can be deleted

  Tests:    7 passed (14 assertions)
  Duration: 0.26s

$ php artisan test --filter=deleted
   PASS  Tests\Feature\TaskTest
  ✓ a task can be deleted                                                          0.11s  

  Tests:    1 passed (3 assertions)
  Duration: 0.15s

7 tests: the 5 task tests plus the 2 examples that come with Laravel. --filter runs only the tests whose names match.7 เทสต์ คือเทสต์ของ tasks 5 ตัว รวมกับเทสต์ตัวอย่าง 2 ตัวที่มากับ Laravel ส่วน --filter รันเฉพาะเทสต์ที่ชื่อตรงกัน

Lesson 9 · Your turnบทที่ 9 · ถึงตาคุณแล้ว

Practiceแบบฝึกหัด

Three small features for the task list. Each solution was added to the real app, with a test, and all 10 tests pass at the end. ฟีเจอร์เล็กๆ สามอย่างสำหรับแอปรายการงาน ทุกเฉลยถูกเพิ่มลงในแอปจริงพร้อมเทสต์ และเทสต์ทั้ง 10 ตัวผ่านในตอนท้าย

1. Count the finished tasks1. นับงานที่เสร็จแล้ว

Change the heading to say, for example, "Tasks (1 of 3 done)". Hint: a Collection has where() and count() too, so you don't need a second query.เปลี่ยนหัวข้อให้แสดง เช่น "Tasks (1 of 3 done)" คำใบ้: Collection ก็มี where() และ count() จึงไม่ต้องคิวรีซ้ำ

Show solutionดูเฉลย

2. No duplicate titles2. ห้ามชื่องานซ้ำ

Refuse a task whose title already exists, with the message "The title has already been taken."ปฏิเสธงานที่มีชื่อซ้ำกับที่มีอยู่แล้ว พร้อมข้อความ "The title has already been taken."

Show solutionดูเฉลย

3. Clear all completed tasks3. ล้างงานที่เสร็จแล้วทั้งหมด

Add a "Clear completed" button that sends DELETE /tasks/completed and deletes every finished task. Watch where you put the route.เพิ่มปุ่ม "Clear completed" ที่ส่ง DELETE /tasks/completed แล้วลบงานที่เสร็จแล้วทั้งหมด ระวังตำแหน่งที่วาง route

Show solutionดูเฉลย

My first attempt added the route at the bottom of the file. The new test failed with a 404, because /tasks/{task} matched first and looked for a task with the id "completed":ครั้งแรกผมเพิ่ม route ไว้ล่างสุดของไฟล์ เทสต์ใหม่ไม่ผ่านด้วย 404 เพราะ /tasks/{task} จับคู่ก่อน แล้วไปหางานที่มี id เป็น "completed":

Route at the bottomวาง route ไว้ล่างสุดLaravel 13.34.0 · PHP 8.3.33
$ php artisan test
   PASS  Tests\Unit\ExampleTest
  ✓ that true is true

   PASS  Tests\Feature\ExampleTest
  ✓ the application returns a successful response                                  0.07s  

   FAIL  Tests\Feature\TaskTest
  ✓ the list shows tasks                                                           0.05s  
  ✓ a task can be created                                                          0.01s  
  ✓ the title is required
  ✓ a task can be marked done
  ✓ a task can be deleted
  ✓ titles must be unique
  ✓ the heading counts done tasks
  ⨯ completed tasks can be cleared                                                 0.01s  
  ──────────────────────────────────────────────────────────────────────────────────────  
   FAILED  Tests\Feature\TaskTest > completed tasks can be cleared                        
  Expected response status code [201, 301, 302, 303, 307, 308] but received 404.
Failed asserting that false is true.

  at tests/Feature/TaskTest.php:84
     80▕         $open = Task::factory()->create(['done' => false]);
     81▕ 
     82▕         $response = $this->delete('/tasks/completed');
     83▕ 
  ➜  84▕         $response->assertRedirect('/tasks');
     85▕         $this->assertDatabaseCount('tasks', 1);
     86▕         $this->assertModelExists($open);
     87▕     }
     88▕ }


  Tests:    1 failed, 9 passed (18 assertions)
  Duration: 0.29s

Moving it above the {task} routes fixes it:ย้ายไปไว้เหนือ route ที่มี {task} ก็แก้ได้:

All three doneทำครบทั้งสามข้อLaravel 13.34.0 · PHP 8.3.33
$ php artisan test
   PASS  Tests\Unit\ExampleTest
  ✓ that true is true

   PASS  Tests\Feature\ExampleTest
  ✓ the application returns a successful response                                  0.07s  

   PASS  Tests\Feature\TaskTest
  ✓ the list shows tasks                                                           0.04s  
  ✓ a task can be created                                                          0.01s  
  ✓ the title is required
  ✓ a task can be marked done
  ✓ a task can be deleted
  ✓ titles must be unique
  ✓ the heading counts done tasks
  ✓ completed tasks can be cleared

  Tests:    10 passed (21 assertions)
  Duration: 0.28s

Lesson 10 · Referenceบทที่ 10 · อ้างอิง

Artisan commandsคำสั่ง Artisan

The commands from this guide, plus a few you'll need soon. php artisan list shows them all, and php artisan help make:model explains any one. คำสั่งที่ใช้ในคู่มือนี้ รวมกับอีกไม่กี่คำสั่งที่จะได้ใช้เร็วๆ นี้ php artisan list แสดงคำสั่งทั้งหมด และ php artisan help make:model อธิบายคำสั่งนั้นๆ

Commandคำสั่งDoesทำอะไร
php artisan servestart the development serverเปิดเซิร์ฟเวอร์สำหรับพัฒนา
php artisan route:listevery route, method and handlerroute ทั้งหมด พร้อม method และตัวจัดการ
php artisan make:model Task -mfmodel + migration + factoryโมเดล + migration + factory
php artisan make:controller TaskControlleran empty controller (--resource adds the 7 standard methods)controller ว่าง (--resource เพิ่ม method มาตรฐาน 7 ตัว)
php artisan make:test TaskTesta feature test (--unit for a unit test)feature test (--unit สำหรับ unit test)
php artisan migraterun new migrationsรัน migration ใหม่
php artisan migrate:rollbackundo the last batchย้อน migration ชุดล่าสุด
php artisan migrate:freshdrop every table and migrate again (deletes all data)ลบทุกตารางแล้ว migrate ใหม่ (ข้อมูลหายหมด)
php artisan tinkeran interactive PHP shell inside your appPHP shell แบบโต้ตอบภายในแอป
php artisan testrun the tests (--filter=name for some)รันเทสต์ (--filter=ชื่อ เพื่อรันบางตัว)
php artisan optimize:clearclear cached config, routes and viewsล้างแคชของ config, route และ view
php artisan aboutversions and settings of this appเวอร์ชันและการตั้งค่าของแอปนี้

Lesson 11 · Debuggingบทที่ 11 · การดีบัก

Common mistakesข้อผิดพลาดที่พบบ่อย

Each of these came up while building this app. The messages are the real ones. ทุกข้อด้านล่างเกิดขึ้นจริงระหว่างสร้างแอปนี้ ข้อความผิดพลาดก็เป็นของจริง

419 Page Expired419 Page Expired

The form has no @csrf, or the session expired. Laravel refuses the POST before your controller runs.ฟอร์มไม่มี @csrf หรือ session หมดอายุ Laravel จะปฏิเสธ POST ก่อนที่ controller จะทำงาน

POST without a tokenPOST โดยไม่มี tokenLaravel 13.34.0 · PHP 8.3.33
$ curl -s -o /dev/null -w "%{http_code}\n" -X POST -d "title=No token" http://127.0.0.1:8000/tasks
419
✗ Wrong✗ ผิด
✓ Right✓ ถูก

"no such table""no such table"

The code is there but the table isn't: the migration hasn't been run on this database. It happens after cloning a project or switching databases. Run php artisan migrate.มีโค้ดแล้วแต่ยังไม่มีตาราง เพราะยังไม่ได้รัน migration กับฐานข้อมูลนี้ มักเกิดหลัง clone โปรเจกต์หรือเปลี่ยนฐานข้อมูล ให้รัน php artisan migrate

A fresh, empty databaseฐานข้อมูลใหม่ที่ยังว่างLaravel 13.34.0 · PHP 8.3.33
$ php artisan tinker --execute="echo App\Models\Task::count();"
   Illuminate\Database\QueryException  SQLSTATE[HY000]: General error: 1 no such table: tasks (Connection: sqlite, Database: database/empty.sqlite, SQL: select count(*) as "aggregate" from "tasks").

A value that silently isn't savedค่าที่ไม่ถูกบันทึกโดยไม่มีการแจ้งเตือน

create() only fills columns listed in #[Fillable]. Anything else is dropped without an error. If a value goes missing, check that list first.create() ใส่ค่าเฉพาะคอลัมน์ที่อยู่ใน #[Fillable] ค่าอื่นจะถูกตัดทิ้งโดยไม่มี error ถ้าค่าหายไป ให้ตรวจรายการนี้ก่อน

secret is not fillablesecret ไม่ได้อยู่ใน fillableLaravel 13.34.0 · PHP 8.3.33
$ php artisan tinker --execute="$(cat tinker-fillable.php)"
{
    "id": 4,
    "title": "Buy milk",
    "done": false,
    "created_at": "2026-10-05T15:15:56.000000Z",
    "updated_at": "2026-10-05T15:15:56.000000Z"
}

405 Method Not Allowed405 Method Not Allowed

The URL exists, but not for this method. Either the route is missing (lesson 8 hit exactly this), or an HTML form is sending POST without @method('PATCH') or @method('DELETE').URL นี้มีอยู่ แต่ไม่รับ method นี้ อาจเป็นเพราะยังไม่มี route (บทที่ 8 เจอแบบนี้พอดี) หรือฟอร์ม HTML ส่ง POST โดยไม่มี @method('PATCH') หรือ @method('DELETE')

✗ Wrong✗ ผิด
✓ Right✓ ถูก

A fixed URL caught by a {parameter} routeURL แบบตายตัวถูก route ที่มี {พารามิเตอร์} จับไปก่อน

Routes are matched from top to bottom. /tasks/{task} also matches /tasks/completed, so the fixed route must come first (practice task 3).route ถูกจับคู่จากบนลงล่าง /tasks/{task} ตรงกับ /tasks/completed ด้วย route แบบตายตัวจึงต้องอยู่ก่อน (แบบฝึกหัดที่ 3)

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Printing with {!! !!}พิมพ์ค่าด้วย {!! !!}

{!! !!} prints raw HTML, so a task titled <script>… would run in every visitor's browser. Use {{ }}, which escapes, for anything a user typed.{!! !!} พิมพ์ HTML ดิบ งานที่ชื่อ <script>… จะทำงานในเบราว์เซอร์ของผู้เข้าชมทุกคน ให้ใช้ {{ }} ซึ่ง escape ให้ กับทุกอย่างที่ผู้ใช้พิมพ์

✗ Wrong✗ ผิด
✓ Right✓ ถูก

Lesson 12 · Referenceบทที่ 12 · อ้างอิง

Glossaryคำศัพท์

Laravel's documentation and error messages use the English terms, so it pays to know both. เอกสารและข้อความแจ้งข้อผิดพลาดของ Laravel ใช้คำภาษาอังกฤษ จึงควรรู้ทั้งสองภาษา

EnglishไทยMeaningความหมาย
frameworkเฟรมเวิร์กReady-made parts and rules for building appsชุดส่วนประกอบและกติกาสำเร็จรูปสำหรับสร้างแอป
routeเส้นทาง (route)A rule linking a method and URL to codeกฎที่จับคู่ method และ URL กับโค้ด
controllerคอนโทรลเลอร์A class that handles requestsคลาสที่จัดการคำขอ
modelโมเดลA class for one database tableคลาสของหนึ่งตารางในฐานข้อมูล
migrationไมเกรชันA PHP file that creates or changes tablesไฟล์ PHP ที่สร้างหรือแก้ไขตาราง
EloquentEloquentLaravel's way to query the database through modelsวิธีคิวรีฐานข้อมูลผ่านโมเดลของ Laravel
BladeBladeLaravel's template language for viewsภาษาเทมเพลตสำหรับ view ของ Laravel
ArtisanArtisanLaravel's command-line tool, php artisanเครื่องมือ command line ของ Laravel คือ php artisan
middlewareมิดเดิลแวร์Code every request passes through, like the CSRF checkโค้ดที่ทุกคำขอต้องผ่าน เช่น การตรวจ CSRF
CSRF tokenCSRF tokenA secret in each form that proves it came from your siteค่าลับในแต่ละฟอร์มที่พิสูจน์ว่ามาจากเว็บของเรา
route model bindingroute model bindingLaravel loading a model from the id in the URLการที่ Laravel โหลดโมเดลจาก id ใน URL ให้
mass assignmentmass assignmentFilling many columns from one array; limited by #[Fillable]การใส่ค่าหลายคอลัมน์จากอาร์เรย์เดียว ซึ่งจำกัดด้วย #[Fillable]
validationการตรวจสอบข้อมูลChecking input against rules before using itการตรวจข้อมูลขาเข้าตามกฎก่อนนำไปใช้
flash dataข้อมูลแฟลชSession data that lasts for one more requestข้อมูลใน session ที่อยู่ได้แค่คำขอถัดไปหนึ่งครั้ง
factoryแฟกทอรีA recipe for fake model data in testsสูตรสร้างข้อมูลปลอมของโมเดลสำหรับเทสต์
feature testfeature testA test that sends fake requests to the appเทสต์ที่ส่งคำขอจำลองเข้าไปในแอป