2019鐵人賽
Laravel
Controller
PHP Magic Methods
Laravel 是一個非常遵守 MVC 設計模式的開發框架,所以為了符合這種設計模式,我們應該使用一個 Controller 的 class 來掌控網路請求的行為,而不是在將所有網路請求的處理邏輯放在 route 檔案中的閉包處理。
我們利用 $ php artisan make:controller YourControllerName
的 Controllers 都存放在 app/Http/Controllers
的資料夾中
我們可以利用 $ php artisan make:controller TestController
創建一個乾淨的控制器。
接著我們在這個控制器裡面新增一個方法,名為:greet
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class TestController extends Controller
{
//
public function greet()
{
echo 'Hello world';
}
}
Route::get('greet', 'TestController@greet');
我們定義控制器路由時,不需要指定完整的控制器命名空間。這是因為 RouteServiceProvider
載入你的路由檔案內的路由組裡已經包含了命名空間,所以我們只要指定 App\Http\Controllers
之後的類別名稱即可。
For Example:
假設你有一個 controller class 在 App\Http\Controllers\Photos\AdminController
,那路由器到控制器的設定如下
Route::get('foo', 'Photos\AdminController@method');
如果你的控制器只需要處理一個動作,那麼你可以直接用 php artisan make:controller --invokeable
創建出來的控制器會包含一個 __invoke
的方法
<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class ShowProfile extends Controller
{
public function __invoke($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
Route::get('user/{id}', 'ShowProfile');
其實 __invoke()
是屬於 PHP 5.3.0 以上版本的語法,當你用類似呼叫函式的方法調用一個 class,__invoke()
這個方法會自動被調用。
如果對於
__invoke
運作模式還有很多疑問的話,請參考 PHP 魔術方法,這一切都是 magic !
我在Day 22 - Laravel Middleware 篇有介紹到,如果你想在控制器的路由中加入中介層,設定方法如下
Route::middleware('auth')->get('profile', 'UserController@show');
//或者是 Route::get('profile', 'UserController@show')->middleware('auth');
但是 Laravel 提供了另外一種更方便的呼叫方法!
Controller
class<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class middleController extends Controller // <<< 必須繼承自 Controller
{
public function show()
{
echo 'show';
}
}
__construct()
,將中介層加入控制器<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class middleController extends Controller // <<< 必須繼承自 Controller
{
public function __construct()
{
// 我這裡延用前天文章中創建的兩個中介層
$this->middleware('before');
$this->middleware('after');
}
public function show()
{
echo 'show';
}
}
__construct()
:當物件生成的同時,強制先去實作 __construct()
內的功能。
關於
__construct()
使用方法有疑惑的建議參考
Laravel 提供了一個更便利的方式來為控制器定義中介層,而不用再定義完整的中介層
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class middleController extends Controller // <<< 必須繼承自 Controller
{
public function __construct()
{
$this->middleware(function ($request, $next)
{
// do something...
return $next($request);
});
}
public function show()
{
echo 'show';
}
}
這個獨立的中介層就只適用這個控制器,如果有其他控制器想使用,就還是乖乖建立一個完整的中介層吧!
在控制器中設定中介層時,可以再加入 only('function name')
或者 except('function name')
來指定或排除需要經過中介層的方法。
For Example:
只有 route 指向 sayHiToAdmin
時才需要經過 IsAdmin
的中介層。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class middleController extends Controller
{
//
public function __construct()
{
$this->middleware('before');
$this->middleware('after');
$this->middleware('Is_Admin')->only('sayHiToAdmin');
}
public function show()
{
echo 'show';
}
public function sayHiToAdmin(Request $request)
{
echo 'Hi, '.$request->name;
}
}
如果要設定一個「CRUD」路由,光是資料庫存取就至少要設定四個路由。Laravel 可以只透過一行程式碼就將典型的「CRUD」路由指定給控制器。
這裡官方文件寫的還滿清楚的,我就不再舉例說明。
Dependency Injection 是一種降低程式高耦合的設計模式,這個概念有點抽象,我不多做說明,我直接解釋他的用法。
有興趣的可以參考
<?php
namespace App\Http\Controllers;
use App\Repositories\UserRepository; // <<< 這裡就是所謂的「相依物件」(Dependency)
class UserController extends Controller
{
//...略
}
__construct()
把相依物件注入 class被宣告的相依物件將會自動解析與注入到控制器實例中
<?php
namespace App\Http\Controllers;
use App\Repositories\UserRepository;
class UserController extends Controller
{
/**
* 使用者 repository 實例
*/
protected $users;
/**
* 建立新控制器實例。
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
}
由於 Laravel 4 只有 constructor injection,所以只要 class 要實現依賴注入,唯一的管道就是 constructor injection,若有些相依物件只有單一 method 使用一次,也必須使用 constructor injection,這將導致最後 constructor 的參數爆炸而難以維護。
對於一些只有單一 method 使用的相依物件,若能只在 method 的參數加上 type-hint,就可自動依賴注入,而不需要動用 constructor,那就太好了,這就是 method injection。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request; // <<< 這裡就是所謂的「相依物件」(Dependency)
class UserController extends Controller
{
public function store(Request $request)
// 不用再透過 '__construct()' 就只要參數前面加入 type-hint 就可以直接將相依物件注入
{
$name = $request->name;
//
}
}
如果預期從 route 會提供一個參數,那麼只要在你其它的依賴之後列出 route 參數即可。
假設我們的 route 設定如下
Route::put('user/{id}', 'UserController@update');
那我們的控制器內的方法設定如下
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function update(Request $request, $id)
{
//
}
}