進入控制器後,有些地方需要一些 PHP 的基本知識點會比較清楚知道程式碼是什麼,畢竟 Laravel 的底層語言就是 PHP
變數 $
$
在 PHP 中只是變數的前綴,所有變數都必須以 $
開始;和 JavaScript 的變數宣告方式不同,但變數的基本概念是一樣的。
$variable = 'Hello, World!';
單引號 '...'
vs 雙引號 "..."
在 PHP 中,單引號和雙引號用來定義字串,但它們有一些不同:
單引號 '...'
: 字串內容會被當作很單純的字串處理,不會解析變數或特殊字符。
雙引號 "..."
: 會解析變數和特殊字符(如 \n
代表換行),有點像 ES6 的樣版自面子。
$name = 'kuku';
$single = 'Hello, $name'; // Hello, $name
$double = "Hello, $name"; // Hello, kuku
陣列 []
在 PHP 中,陣列可以用來存儲多個值,可以是索引陣列或關聯陣列。
// 索引陣列 -> 跟 JavaScript 的陣列很相似用數字索引
$colors = array('Red', 'Green', 'Blue');
// 關聯陣列 -> 跟 JavaScript 的物件很相似使用 key/ value
$person = array('first_name' => 'ting', 'last_name' => 'ku');
依賴注入
依賴注入是一種設計模式,用來管理對象之間的依賴關係。
在 PHP 中,特別是在 Laravel 框架中,這通常通過服務容器(Service Container)來實現。
class UserController extends Controller
{
protected $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
}
控制器是一種組織應用邏輯的方式,它將請求處理邏輯從路由中抽離,讓代碼更具可維護性和結構化。
控制器通常包含多個方法,每個方法負責處理不同的請求或功能。
創建控制器
使用 Artisan 命令 php artisan make:controller 控制器的檔案名字
來創建控制器,例如:創建一個名為 UserController 的控制器,下指令: php artisan make:controller UserController
,就會在 app/Http/Controllers
目錄下生成 UserController.php 文件。
使用控制器
<?php
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* 顯示所有用戶列表的方法
*
* @return string
*/
public function index(): string
{
return '我是 UserController 的 index 方法';
}
/**
* 顯示用於建立使用者的表單
*
* @param int|null $id
* @return string
*/
public function show(int $id = null): string
{
return "Showing item with ID: {$id}";
}
/**
* 把新建立的使用者資料儲存在資料庫中
*
* @param Request $request
* @return JsonResponse
*/
public function store(Request $request): JsonResponse
{
// 返回一個 JSON 響應,顯示提交的數據
return response()->json([
'message' => 'Data stored successfully',
'data' => $request->all()
]);
}
/**
* 更新儲存中的指定使用者
*
* @param Request $request
* @param $id
* @return JsonResponse
*/
public function update(Request $request, $id): JsonResponse
{
// 返回一個 JSON 響應,顯示更新的數據
return response()->json([
'message' => 'Data updated successfully',
'id' => $id,
'data' => $request->all()
]);
}
/**
* 從儲存中刪除指定的使用者
*
* @param int|null $id
* @return string
*/
public function destroy(int $id = null): string
{
// 返回一個文本響應,確認刪除操作
return "Deleted item with ID: {$id}";
}
}
Controller 只有一個行為寫法 __invoke()
如果建立的控制器只有使用單一
方法(Single Action Controllers),可以使用 __invoke()
方法。
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
class ShowProfileController extends Controller
{
public function __invoke($userId)
{
$user = User::findOrFail($userId);
return view('user.profile', ['user' => $user]);
}
}
Route 裡面也只要寫上 Controller 的名字就可以抓到 Controller 的資料。
use App\Http\Controllers\ShowProfileController;
Route::get('/user/{userId}', ShowProfileController::class);
Controller middleware用法
第二天有提到請求的生命週期:Request(打球) -> index.php(找球員) -> Kernel(賽程表) -> Router(踢球) -> Middleware(守門員) -> Controller(進網得分);
但是在實例化的先後順序是:Router(球) -> Controller(網子) -> Middleware(守門的人);所以在控制器中使用 middleware,要特別留意實例化順序。
使用 __construct()
建構函式指定中間件
class UserController extends Controller
{
/**
* Instantiate a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
// only:只包含指定的動作,也就是只做 'index'
$this->middleware('log')->only('index');
// except:排除指定的動作,也就是除了 'store' 其他都不做
$this->middleware('subscribed')->except('store');
}
}
🔔 Controller 抓不到 Middleware 在
__construct()
中建立的快取資料,因為 Controller 會比 Middleware 早實例化。
🔔 如果畫面或是 postman 噴錯提示有關實例化,可以看相關檔案的__construct()
生命週期。
使用閉包註冊 middleware
$this->middleware(function ($request, $next) {
// ...
return $next($request);
});
在路由文件 routes/api.php
中,可以將路由與控制器方法關聯,目前累積有 3 種寫法:
關聯寫法
Route::get('/user', [UserController::class, 'index']);
Route::get('/user/{id}', [UserController::class, 'show']);
Route::post('/user', [UserController::class, 'store']);
Route::put('/user/{id}', [UserController::class, 'update']);
Route::delete('/user/{id}', [UserController::class, 'destroy']);
在 postman 試打註冊的路由,理論上可以得到以下結論:
GET /user
路由 -> '我是 UserController 的 index 方法'。
GET /user/{id}
-> 可以看到變數 $user 以及 'Showing item with ID: {id}'。
POST /user
-> 數據的 JSON 響應。
PUT /user/{id}
-> 更新數據的 JSON 響應。
DELETE /user/{id}
-> Deleted item with ID: {id} 。
之前版本的寫法
用 @
的字串寫法,也可以使用 namespace
的方法給控制器位置,這是舊版本的寫法。
Route::get('/user', '\App\Http\Controllers\UserController@index');
抽共用的寫法
把共用的內容都抽到最外面,雖然可以整理的很乾淨,但是 IDE 無法支援立即跳轉到對應方法,所以鮮少使用。
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Route;
Route::controller(UserController::class)->prefix('user')->group(function () {
Route::get('/', 'index');
Route::get('/{id}', 'show');
Route::post('/', 'store');
Route::put('/{id}', 'update');
Route::delete('/{id}', 'destroy');
});
資源控制器(Resource Controllers)是 Laravel 提供的一種便捷方式,用來處理具有 CRUD(Create, Read, Update, Delete)操作的路由和控制器邏輯。使用資源控制器,可以簡化對常見操作的處理,並自動生成相應的路由和方法。
創建資源控制器
下指令 php artisan make:controller 資源控制器檔案名稱 --resource
,套件就會自動創建一個包括基本 CRUD 方法的控制器,例如 index、create、store、show、edit、update 和 destroy 方法。
註冊資源路由
使用 Route::resource
就可以註冊資源控制器的路由,而且這個單一的路由可以創建了多個
路由來處理資源上的各種行為。
// api.php 中註冊資源路由
Route::resource('posts', PostController::class);
可以透過指令 php artisan route:list
快速了解應用程式,確認目前生成的 api 有哪幾支
每支路由都會有自己對應的控制器方法,以下是對照表,這樣就可以在控制器中找到對應的方法,把想要請他執行的邏輯寫在裡面
Verb | URI | Action | Route Name |
---|---|---|---|
GET | /post |
index | post.index |
GET | /post/create |
create | post.create |
POST | /post |
store | post.store |
GET | /post/{post} |
show | post.show |
GET | /post/{post}/edit |
edit | post.edit |
PUT/PATCH | /post/{post} |
update | post.update |
DELETE | /post/{post} |
destory | post.destory |
資源控制器範例
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
// 對應的 URI GET -> /items
public function index()
{
$posts = Post::all();
return view('posts.index', compact('posts'));
}
// 對應的 URI GET -> /items/create
public function create()
{
return view('posts.create');
}
// 對應的 URI POST -> /items
public function store(Request $request)
{
Post::create($request->all());
return redirect()->route('posts.index');
}
// 對應的 URI GET -> /items/{id}
public function show(Post $post)
{
return view('posts.show', compact('post'));
}
// 對應的 URI GET -> /items/{id}/edit
public function edit(Post $post)
{
return view('posts.edit', compact('post'));
}
// 對應的 URI PUT/PATCH -> /items/{id}
public function update(Request $request, Post $post)
{
$post->update($request->all());
return redirect()->route('posts.index');
}
// 對應的 URI DELETE -> /items/{id}
public function destroy(Post $post)
{
$post->delete();
return redirect()->route('posts.index');
}
}
延續前一天任務,建立一個控制器並且 dd 出前端帶給後端的會員帳號(暫定 $account)
用指令 php artisan make:controller UserController
建立控制器,把前一天路由閉包的內容搬到控制器各個方法中
<?php
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* 建立使用者資訊到資料庫
*
* @param Request $request
* @return string
*/
public function createUserInformation(Request $request): string
{
return 'register_success';
}
/**
* 處理登入驗證(規劃 - 成功跳轉頁面,失敗轉回登入頁面)
*
* @param Request $request
* @return string
*/
public function handelLogin(Request $request): string
{
$name = $request->input('account');
return "{$name}_login_success";
}
/**
* 處理忘記密碼流程
*
* @param Request $request
* @param $account
* @return string
*/
public function handelForgot(Request $request, $account = null): string
{
damp($account);
return "{$account}_forget";
}
}
在 routes/api.php
把路由的閉包修改成控制器方法與路由的關聯
<?php
use App\Http\Controllers\UserController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/
// 在這個組中的路由都會應用註冊、登入、忘記密碼
Route::prefix('auth')->group(function () {
Route::post('/register', [UserController::class, 'createUserInformation'])
->name('auth.register');
Route::post('/login', [UserController::class, 'handelLogin'])
->name('auth.login');
Route::get('/forget/{account?}', [UserController::class, 'handelForgot'])
->whereAlphaNumeric('account')
->name('auth.forget');
});