如何從 Route 傳參數給 Controller?
實務上我們可能會遇到資料庫邏輯與商業邏輯完全相同,只有顯示邏輯不同,也就是 controller、service、repository 完全相同,只有 route 與 view 不相同,我們該如何在使用同一個 controller 的前提下,實現此需求呢?
Version
Laravel 5.2.29
測試案例
我們以 TDD 方式來完成此需求。
- URI 為
/welcome
時,使用welcome.blade.php
。 - URI 為
/helloworld
時,使用helloworld.blade.php
。
第一個測試
HomeControllerTest.php1 1GitHub Commit : 新增HomeControllerTest的第一個測試1
2
3
4
5
6
7
8
9class HomeControllerTest extends TestCase
{
/** @test */
public function welcomeURI()
{
$this->visit('/welcome')
->see('Laravel 5');
}
}
我們希望當URI為 /welcome
時,期望能在 view 看到 Laravel 5
字串。
馬上跑測試,我們得到第1個 紅燈。
錯誤訊息為 A request [http://localhost/welcome] failed
。
因為我們還沒有建立 route。
routes.php2 2GitHub Commit : 修改routes.php,增加/welcome1
2
3
4
5
6
7
8Route::get('/', function () {
return view('welcome');
});
Route::get('/welcome', [
'as' => 'Welcome',
'uses' => 'HomeController@index'
]);
補上 route 後,繼續跑測試。
我們得到第2個 紅燈。
錯誤訊息為 Class App\Http\Controllers\HomeController does not exist
。
因為我們還沒建立 HomeController
。
HomeController.php3 3GitHub Commit : 新增HomeController.php1
2
3
4
5
6
7
8
9
10
11namespace App\Http\Controllers;
use App\Http\Requests;
class HomeController extends Controller
{
public function index()
{
return view('Welcome');
}
}
新增 HomeController
後,繼續跑測試。
我們得到第1個 綠燈。
第二個測試
HomeControllerTest.php4 4GitHub Commit : 新增HomeControllerTest的第二個測試1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class HomeControllerTest extends TestCase
{
/** @test */
public function welcomeURI()
{
$this->visit('/welcome')
->see('Laravel 5');
}
/** @test */
public function helloWorld()
{
$this->visit('/helloworld')
->see('Hello World');
}
}
馬上跑測試,我們得到第1個 紅燈。
錯誤訊息為 A request [http://localhost/helloworld] failed
。
因為我們還沒有建立 route。
routes.php5 5GitHub Commit : 修改routes.php,增加/helloworld1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17use App\Http\Controllers\HomeController;
Route::get('/', function () {
return view('welcome');
});
Route::get('/welcome', [
'as' => 'Welcome', function () {
return App::make(HomeController::class)->index('welcome');
}
]);
Route::get('/helloworld', [
'as' => 'HelloWorld', function () {
return App::make(HomeController::class)->index('helloworld');
}
]);
修改 routes 時,我們面臨了一個挑戰,需求為 route 與 view 不同,但 controller 相同,因此勢必使用同一個 HomeController@index
。
也就是若我們可以將 view 當成參數,從 route 傳給 HomeController@index
,就能達成我們的需求。
我們將陣列的 key 由 uses
改成 closure ,利用App::make()
自己建立 HomeController
物件,並將 view 為 index()
的參數傳入。
HomeController.php6 6GitHub Commit : 修改HomeController.php,新增$viewName參數1
2
3
4
5
6
7
8
9
10
11namespace App\Http\Controllers;
use App\Http\Requests;
class HomeController extends Controller
{
public function index($viewName)
{
return view($viewName);
}
}
由於 routes.php
將 view的名稱傳入 HomeController@index
,所以必須重構符合 routes.php
的要求。
重構 HomeController.php
後,繼續跑測試。
我們得到第2個 紅燈。
錯誤訊息為 View [HelloWorld] not found
。
因為我們還沒建立 helloworld.blade.php
。
新增helloworld.blade.php
後,繼續跑測試。
我們得到2個 綠燈,兩個測試都通過了。
Conclusion
- 我們也可以自行由
routes.php
去建立controller
物件,只是因為 Laravel 內部大量使用依賴注入,所以你無法自行使用new
去建立,但透過App::make()
與 service container,我們就可以再次掌握 controller 物件,因此可以透過 route 對 controller 傳參數。
Sample Code
完整的範例可以在我的GitHub上找到。