控制器
新建控制器文件 app/controller/FooController.php。
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function index(Request $request)
{
return response('hello index');
}
public function hello(Request $request)
{
return response('hello webman');
}
}
當訪問 http://127.0.0.1:8787/foo 時,頁面返回 hello index。
當訪問 http://127.0.0.1:8787/foo/hello 時,頁面返回 hello webman。
當然你可以透過路由配置來更改路由規則,參見路由。
提示
如果出現404無法訪問,請打開config/app.php,將controller_suffix設置為Controller,並重啟。
控制器後綴
webman從1.3版本開始,支持在config/app.php設置控制器後綴,如果config/app.php裡controller_suffix設置為空'',則控制器類似如下
app\controller\Foo.php。
<?php
namespace app\controller;
use support\Request;
class Foo
{
public function index(Request $request)
{
return response('hello index');
}
public function hello(Request $request)
{
return response('hello webman');
}
}
強烈建議將控制器後綴設置為Controller,這樣能避免控制器與模型類名衝突,同時增加安全性。
說明
- 框架會自動向控制器傳遞
support\Request對象,通過它可以獲取用戶輸入數據(get post header cookie等數據),參見請求 - 控制器裡可以返回數字、字符串或者
support\Response對象,但是不能返回其它類型的數據。 support\Response對象可以通過response()json()xml()jsonp()redirect()等助手函數創建。
控制器參數綁定
例子
webman支持通過控制器方法參數自動綁定請求參數,例如
<?php
namespace app\controller;
use support\Response;
class UserController
{
public function create(string $name, int $age): Response
{
return json(['name' => $name, 'age' => $age]);
}
}
你可以通過GET POST方式傳遞name和age的值,也可以通過路由參數傳遞name和age參數,例如
Route::any('/user/{name}/{age}', [app\controller\UserController::class, 'create']);
優先級為路由參數 > GET > POST參數
默認值
假設我們訪問 /user/create?name=tom,我們將得到如下的錯誤
Missing input parameter age
原因是我們沒有傳遞age參數,可以通過給參數設置默認值來解決這個問題,例如
<?php
namespace app\controller;
use support\Response;
class UserController
{
public function create(string $name, int $age = 18): Response
{
return json(['name' => $name, 'age' => $age]);
}
}
參數類型
當我們訪問 /user/create?name=tom&age=not_int,我們將得到如下的錯誤
提示
這裡為了方便測試,我們直接在瀏覽器地址欄輸入參數,實際開發中應該通過POST方式傳遞參數
Input age must be of type int, string given
這是因為接受的數據會按照類型進行轉換,如果無法轉換則會拋出support\exception\InputTypeException異常,
因為傳遞的age參數無法轉換為int類型,所以得到如上錯誤。
自定義錯誤
我們可以利用多語言自定義Missing input parameter age 和 Input age must be of type int, string given 這樣的錯誤,
參考如下命令
composer require symfony/translation
mkdir resource/translations/zh_CN/ -p
echo "<?php
return [
'Input :parameter must be of type :exceptType, :actualType given' => '輸入參數 :parameter 必須是 :exceptType 類型,傳遞的類型是 :actualType',
'Missing input parameter :parameter' => '缺少輸入參數 :parameter',
];" > resource/translations/zh_CN/messages.php
php start.php restart
其它類型
webman支持的參數類型有int float string bool array object 類實例等,例如
<?php
namespace app\controller;
use support\Response;
class UserController
{
public function create(string $name, int $age, float $balance, bool $vip, array $extension): Response
{
return json([
'name' => $name,
'age' => $age,
'balance' => $balance,
'vip' => $vip,
'extension' => $extension,
]);
}
}
當我們訪問 /user/create?name=tom&age=18&balance=100.5&vip=true&extension[foo]=bar,我們將得到如下的結果
{
"name": "tom",
"age": 18,
"balance": 100.5,
"vip": true,
"extension": {
"foo": "bar"
}
}
類實例
webman支持通過參數類型提示傳遞類實例,例如
app\service\Blog.php
<?php
namespace app\service;
class Blog
{
private $title;
private $content;
public function __construct(string $title, string $content)
{
$this->title = $title;
$this->content = $content;
}
public function get()
{
return [
'title' => $this->title,
'content' => $this->content,
];
}
}
app\controller\BlogController.php
<?php
namespace app\controller;
use app\service\Blog;
use support\Response;
class BlogController
{
public function create(Blog $blog): Response
{
return json($blog->get());
}
}
當訪問 /blog/create?blog[title]=hello&blog[content]=world,我們將得到如下的結果
{
"title": "hello",
"content": "world"
}
模型實例
app\model\User.php
<?php
namespace app\model;
use support\Model;
class User extends Model
{
protected $connection = 'mysql';
protected $table = 'user';
protected $primaryKey = 'id';
public $timestamps = false;
// 這裡需要添加可填充字段,防止前端傳入不安全字段
protected $fillable = ['name', 'age'];
}
app\controller\UserController.php
<?php
namespace app\controller;
use app\model\User;
class UserController
{
public function create(User $user): int
{
$user->save();
return $user->id;
}
}
當訪問 /user/create?user[name]=tom&user[age]=18,我們將得到類似如下的結果
1
控制器生命週期
當config/app.php裡controller_reuse為false時,每個請求會初始化一次對應的控制器實例,請求結束後控制器實例銷毀,這與傳統框架運行機制相同。
當config/app.php裡controller_reuse為true時,所有請求將重用控制器實例,也就是控制器實例一旦創建便常駐內存,所有請求重用。
注意
開啟控制器重用時,請求不應該更改控制器的任何屬性,因為這些更改將影響後續請求,例如
<?php
namespace app\controller;
use support\Request;
class FooController
{
protected $model;
public function update(Request $request, $id)
{
$model = $this->getModel($id);
$model->update();
return response('ok');
}
public function delete(Request $request, $id)
{
$model = $this->getModel($id);
$model->delete();
return response('ok');
}
protected function getModel($id)
{
// 該方法將在第一次請求 update?id=1 後會保留下 model
// 如果再次請求 delete?id=2 時,會刪除 1 的數據
if (!$this->model) {
$this->model = Model::find($id);
}
return $this->model;
}
}
提示
在控制器__construct()構造函數中return數據不會有任何效果,例如
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function __construct()
{
// 構造函數中return數據沒有任何效果,瀏覽器不會收到此響應
return response('hello');
}
}
控制器不重用與重用區別
區別如下
不重用控制器
每個請求都會重新new一個新的控制器實例,請求結束後釋放該實例,並回收內存。不重用控制器和傳統框架一樣,符合大部分開發者習慣。由於控制器反復的創建銷毀,所以性能會比重用控制器略差(helloworld壓測性能差10%左右,帶業務可以基本忽略)
重用控制器
重用的話一個進程只new一次控制器,請求結束後不釋放這個控制器實例,當前進程的後續請求會重用這個實例。重用控制器性能更好,但是不符合大部分開發者習慣。
以下情況不能使用控制器重用
當請求會改變控制器的屬性時,不能開啟控制器重用,因為這些屬性的更改會影響後續請求。
有些開發者喜歡在控制器構造函數__construct()里針對每個請求做一些初始化,這時候就不能重用控制器,因為當前進程構造函數只會調用一次,並不是每個請求都會調用。