การจัดการกับการทำงานภายในระบบ ซึ่งสามารถใช้ เส้นทางแล้วตามด้วยตัวแปรฟังก์ชัน (closures) ซึ่งก็ทำงานได้ แต่ถ้าต้องการแยกการทำออกเป็นกลุ่มงานเฉพาะ หรือแยกออกจากเส้นทาง ก็สามารถทำได้เป็นโดยใช้คอนโทรลเลอร์ คอนโทรลเลอร์เป็นคลาสที่รวมกับความสัมพันธ์ที่เกี่ยวข้องกันให้อยู่ในที่เดียวกัน ทำให้ง่ายแต่การจัดการดูแลระบบเว็บได้ดี โดยค่าปริยายคอนโทรลเลอร์เก็บไว้ที่โฟลเดอร์ app/Http/Controllers
สร้างคอนโทรลเลอร์
การสร้างคอนโทรลเลอร์วิธีที่ง่ายที่สุดคือ สร้างผ่าน CLI ซึ่งจะได้คลาสคอนโทรลเลอร์ การตั้งชื่อคลาสควรใช้อักษรแรกเป็นพิมพ์ใหญ่ตามธรรมเนียมการตั้งชื่อคลาส
> php artisan make:controller HomeController
เมื่อสร้างคอนโทรลเลอร์แล้วจะได้โครงสร้างของคลาส ที่มาพร้อมกับ namespace และคลาสที่จำเป็นต้องใช้ อย่างไรก็ตาม ให้สร้างฟังก์ชัน home( ) เพิ่ม ดังตัวอย่างต่อไปนี้
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller{
public function home(){
return "<h2>Home</h2>";
}
}
ฟังก์ชัน home( ) ไม่ได้มีอะไรมากนอกจากจะคืนค่า อักษร เพื่อแสดงผลที่หน้าเว็บ
ต่อไปก็ให้สร้างเส้นทาง ให้เพิ่มเติมที่ไปยังคอนโทรลเลอร์นี้ โดยให้การเรียกใช้ฟังก์ชัน home() และต้องไม่ลืมนำเข้า (use) HomeController ด้วย
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HomeController;
Route::get('/', function () {
return view('welcome');
});
Route::get('/home', [HomeController::class, 'home']);
เมื่อทดสอบหน้าเว็บ http://localhost:8000/home ก็จะได้ค่าอักษรที่คืนมาจากคอนโทรลเลอร์
คอนโทรลเลอร์อ่านแบบจำลอง
สร้างไฟล์ในชื่อ UserController.php การสร้างจะสร้างแค่ User ก็ได้ แต่ที่เลือกให้มีคำว่า Controller ต่อท้ายด้วยเพื่อจะไม่ได้สับสนในการอ้างอิงที่ได้ประเภทของไฟล์มาด้วย ชื่อไฟล์ก็จะกลายเป็นชื่อคลาสไปด้วย ให้มี namespace ในลำดับของ App\Http\Controllers ซึ่งใช้อักษรตัวใหญ่ หรือจะสร้างผ่าน CLI ก็ได้
> php artisan make:controller UserController
> php artisan make:model UserModel
คลาสของ UserController มีการสืบทอดจาก Controller เป็นมาตรฐาน และสร้างฟังก์ชัน show($id) รับตัวแปรหนึ่งตัว เพื่อใช้ในการสืบค้น รายชื่อผู้ใช้ตามรหัส (id) การคืนค่าของฟังก์ชันนี้จะคืนค่า view(A, B) โดยมีตัวแปรสองตัว ตัวแปรแรกแทน HTML Template ที่ชื่อ user ซึ่งคือไฟล์ user.blade.php นั้นเอง ส่วนตัวแปรที่สองเป็นอาร์เรย์ของข้อมูลในรูปแบบอาร์เรย์แบบมีคีย์ (Associative Array)
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\UserModel;
class UserController extends Controller
{
public function show($id)
{
return view('user', [
'user' => UserModel::find($id)
]);
}
}
จากตัวอย่างนี้ อาร์เรย์แบบมีคีย์ โดยมีคีย์ชื่อ user ที่มาจาก UserModel ซึ่งเป็นคลาสที่มาจาก Model และเรียกใช้ฟังก์ชัน find($id) ที่มีตัวแปร $id หนึ่งตัว ตัวแปรนี้ส่งผ่านเส้นทาง ดังนั้นคอนโทรลเลอร์จึงมีการเรียกใช้จากเส้นทาง ดังให้เพิ่ม เส้นทางใหม่นี้
use App\Http\Controllers\UserController;
Route::get('/user/{id}', [UserController::class, 'show']);
จากตัวอย่างเส้นทางนี้ ใช้เส้นทาง /user/{id} เป็น URI ที่มีตัวแปร {id} ติดมาด้วย ตัวแปรนี้จะส่งต่ออัตโนมัติเข้าฟังก์ชัน show ของคอนโทรลเลอร์ UserController
สำหรับ UserModel เป็นแบบจำลองที่สมมุติให้ส่งข้อมูล ซึ่งอาจมาจากฐานข้อมูล แต่ในตอนนี้ให้สมมุติให้มาจากอาร์เรย์ดังนี้
<?php
namespace App\Models;
class UserModel{
const USERS = array(
array('id' => 1, 'name' => 'Pol L.', 'tel' => "021234567"),
array('id' => 2, 'name' => 'Mon L.', 'tel' => "021234568"),
array('id' => 3, 'name' => 'Tee K.', 'tel' => "021234569"),
);
public static function find($id) {
$user = null;
foreach (self::USERS as $u) {
if ($u['id'] == $id) {
$user = $u;
break;
}
}
return $user;
}
public static function all(){
return self::USERS;
}
}
จาก URI ที่ระบุเส้นทาง /user/{id} นั้น ในส่วน user หมายถึงชื่อ view ดังนั้นจะต้องสร้าง view นี้ หากจำกันได้ข้อมูลที่ส่งมาจากคอนโทรลเลอร์ ในชื่อ user ดังนั้นเรานำชื่อนี้ไปแสดงผลได้
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>User</title>
<style>
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
<div class="content">
<h1>User</h1>
{{$user['id']}} {{$user['name']}} TEL:{{$user['tel']}}
</div>
</div>
</body>
</html>
เมื่อทุกอย่างเขียนเตรียมไว้หมดแล้ว ทดสอบให้อ่านรายชื่อเลขที่ 1 ผ่าน URL: http://localhost:8000/user/1
สร้างคอนโทรลเลอร์ปริยาย
ปริยายนี้หมายถึงค่า default หรือค่าที่ไม่ต้องเรียกชื่อฟังก์ชัน ก็จะหมายถึงฟังก์ชันปริยายในคอนโทรลเลอร์ ซึ่งฟังก์ชันนี้คือ __invoke( )
public function __invoke(){ }
สมมุติให้ฟังก์ชันนี้ อ่านค่าทั้งหมดของรายการ user การสร้างฟังก์ชันนี้ เท่าที่ทดลองทำ สามารถสร้างไฟล์ขึ้นเองได้ หรือใช้การสร้างผ่าน CLI ก็ได้
> php artisan make:controller UsersController --invokable
ผลจากใช้ CLI นี้จะสร้างไฟล์ UsersController.php มาให้ พร้อมกับฟังก์ชัน __invoke( ) มาให้ เราเพียงเติมเต็มการทำงานให้ดึงข้อมูลมาแสดงใน view ชื่อ users
<?php
namespace App\Http\Controllers;
use App\Models\UserModel;
use Illuminate\Http\Request;
class UsersController extends Controller
{
public function __invoke(Request $request)
{
return view('users', [
'users'=> UserModel::all()
]);
}
}
ฟังก์ชัน __invoke(Request $request) มีใส่ตัวแปรเข้ามาด้วย แต่ในตัวอย่างไม่ได้นำไปใช้ประโยชน์อะไร ณ ขณะนี้
ต่อมาเส้นทางไปยัง /users โดยไม่จำเป็นต้องระบุชื่อเมธอดอีกต่อไป แต่มีข้อแตกต่างตรงที่ไม่ได้อยู่ในเครื่องหมาย [ ] อีกต่อไป
use App\Http\Controllers\UsersController;
Route::get('/users', UsersController::class);
ที่เหลือคือหน้า view:users ที่แสดงรายการทั้งหมดของผู้ใช้ (users) ที่ส่งข้อมูลจากคอนโทรลเลอร์ ตัวอย่างแสดงผลต่อไปนี้แสดงรายการผู้ใช้ทั้งหมดในรูปตาราง (ใช้ Bootstrap ประกอบการจัดรูปตาราง ซึ่งไม่ใช้รูปตาม bootstrap ก็ได้) และทดสอบผ่าน URL: http://localhost:8000/users
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,
initial-scale=1">
<title>User</title>
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
</head>
<body>
<div class="flex-center position-ref full-height">
<div class="content">
<h1>User</h1>
<table class='table'>
<thead>
<tr><th>Id</th><th>Name</th><th>Tel</th></tr>
</thead>
<tbody>
@php
foreach($users as $user){
echo("<tr>");
echo("<td>". $user['id']. "</td>");
echo("<td>". $user['name']."</td>");
echo("<td>". $user['tel'] ."</td>");
echo("</tr>");
}
@endphp
</tbody>
</table>
</div>
</div>
</body>
</html>
รีซอร์สคอนโทรลเลอร์ (Resource Controller)
ในแบบจำลองข้อมูลของ Eloquent ทำตัวเสมือนเป็น รีซอร์ส (resource) ซึ่งหมายความว่า จะมีต้นแบบของฟังก์ชันอยู่ในคอนโทรลเลอร์อยู่แล้ว ยกตัวอย่างเช่น เมื่อเราต้องการสร้าง รีซอร์สคอนโทรลเลอร์ ProductController ด้วยคำสั่ง: CLI 4
> php artisan make:controller ProductController --resource
ด้วย CLI นี้ของลาราเวล จะทำให้คลาส ProductController ที่มีฟังก์ชันมาตรฐานตามการดำเนินงานกับรีซอร์ส และฟังก์ชันเหล่านี้ทำงานได้ดีกับการจัดการฐานข้อมูลที่อยู่ในกรอบงาน Eluquent
สำหรับการเส้นทางตาม URI ที่ใช้ในไฟล์ web.php ก็ใช้เพียงการลงทะเบียน
use App\Http\Controllers\ProductController;
Route::resource('products', ProductController::class);
ด้วยการลงทะเบียนเพียงแค่นี้ ก็ถือว่าได้ลงทะเบียนเส้นทางมาตรฐานไปด้วย ดูเส้นทางมาตรฐานตาม เช่น ถ้าผู้ใช้ไปที่ URL: /products จะหมายถึง การส่งคำขอแบบ GET และจะเรียกฟังก์ชัน index( ) ในคลาสคอนโทรลเลอร์ให้ทำงาน หรือถ้าผู้ใช้ส่งคำขอแบบ POST ด้วย URL เดิมจะเป็นการเรียกฟังก์ชัน store( ) ให้ทำงาน
ตาราง 1. ฟังก์ชันมาตรฐานของรีซอร์สVerb | URI | Action | Route Name |
---|---|---|---|
GET | /products | index | products.index |
GET | /products/create | create | products.create |
POST | /products | store | products.store |
GET | /products/{product} | show | products.show |
GET | /products/{product}/edit | edit | products.edit |
PUT/PATCH | /products/{product} | update | products.update |
DELETE | /products/{product} | destroy | products.destroy |
เพื่อทดสอบการใช้งานเส้นทางตาม รีซอร์สคอนโทรลเลอร์ ว่าใช้งานได้จริงหรือไม่ ให้สมมุติการคืนค่าของฟังก์ชันที่รองรับคำขอแบบ GET บางฟังก์ชัน
public function index(){
return "<h2>Product index</h2>";
}
public function create(){
return "<h2>Product create</h2>";
}
public function show($id){
return "<h2>Product show ". $id."</h2>";
}
จากสามฟังก์ชันนี้ใช้ผ่านคำขอ GET ทั้งหมด เราจะทดสอบคำขอเรียงตามลำดับฟังก์ชัน ซึ่งจะคืนค่าข้อมูลที่เขียนตามแต่ละฟังก์ชัน
- http://localhost:8000/products
- http://localhost:8000/products/create
- http://localhost:8000/products/1
แน่นอนว่าได้ผลที่แนะตาม ตาราง 1 ซึ่งนับว่าประหยัดเวลาการเขียนโปรแกรมได้มาก นับเป็นความฉานฉลาดของลาราเวล แล้วจะได้พบการใช้แน่นอนกับรูปแบบคอนโทรลเลอร์นี้ที่ใช้งานคู่กับฐานข้อมูล
ยังมีรีซอร์สอีกประเภทที่เรียกว่า apiResource ซึ่งก็ทำงานได้เหมือนรีซอร์สคอนโทรลเลอร์ เพราะถูกสร้างในแบบเดียวกับ รีซอร์สคอนโทรลเลอร์ เพียงแต่มีสองฟังก์ชันไม่ถูกสร้างมาด้วย (edit( ) กับ create( )) อย่างไรก็ตามก็เขียนเพิ่มเติมให้เหมือนรีซอร์สคอนโทรลเลอร์ได้ การสร้าง apiResource เพื่อต้องการสร้าง API ขึ้นเองโดยไม่จำเป็นต้องทำตามแบบ รีซอร์สที่ทำงานคู่กับฐานข้อมูลอย่าง Eloquent
สมมติว่าต้องการสร้าง สร้าง apiResource ขึ้นเองผ่านการใช้งานคอนโทรลเลอร์ในชื่อ BookController
> php artisan make:controller BookController --api
สำหรับการลงทะเบียนเส้นทาง จะลงทะเบียนผ่านไฟล์ web.php ก็ได้ ซึ่งจะทำงานเหมือน รีซอร์สคอนโทรลเลอร์ หรือจะลงทะเบียนผ่านไฟล์ api.php
เพื่อให้เกิดความแตกต่างจากตัวอย่างที่ผ่านมา ให้ลงทะเบียนผ่านไฟล์ api.php และใช้ฟังก์ชัน apiResourece() ในการลงทะเบียน โดยการเขียนเพิ่มเติมตามตัวอย่างนี้
use App\Http\Controllers\BookController;
Route::apiResource('books', BookController::class);
ในการทดสอบใช้การทดสอบเหมือนกับตัวอย่างที่ผ่านมา แต่ที่ต่างไปจากเดิม คือการใช้ URL ที่เพิ่มคำว่า “api” ดังตัวอย่าง URL: http://localhost:8000/api/books/1 ด้วย URL ใหม่นี้ก็จะเป็นการเรียกฟังก์ชัน show($id) ของ BookController
Middleware ในคอนโทรลเลอร์
เมื่อต้องการใช้สื่อตัวกลางกับคอนโทรลเลอร์ สามารถเชื่อมต่อท้ายกับเส้นทางได้เลย เช่น ต้องการให้ UsersController ต้องผ่านการตรวจสอบสิทธิ์ก่อน
ตัวอย่างเช่น ให้ UsersController มีการใช้ให้ middleware(‘auth’) ทำงานก่อน สำหรับสื่อตัวกลางนี้มีให้มาแล้วในไฟล์ชื่อ Authenticate.php และลงทะเบียนแล้วที่ไฟล์ Kernel.php ในชื่อ auth ดังนั้นการทำให้คอนโทรลเลอร์เรียกใช้สื่อตัวกลางก่อนจะต้องลงทะเบียนเส้นทาง
Route::get('/users', 'UsersController')->middleware('auth');
//และลงทะเบียนเส้นทาง login
Route::get('/login', function(){
return view('login');
})->name('login');
และจากการตรวจสอบไฟล์ Authenticate.php มีฟังก์ชัน redirectTo( ) ที่ใช้ตรวจสอบการตอบสนองในรูปแบบ JSON หรือไม่ ถ้าไม่ใช่จะส่งไปยังเส้นทาง login
สำหรับเส้นทาง login มีการตั้งชื่อเส้นทางไว้ด้วยตามชื่อเส้นทาง และจะส่งไปเรียกหน้า view: login.blade.php เราอาจทดลองสร้างรอไว้เพื่อการทดสอบ
ทดสอบการใช้งานกับ URL: http://localhost:8000/users จะพบว่าจะได้ไปที่ http://localhost:8000/login แทน