コントローラー
新しいコントローラーファイル 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;
// セキュリティのためにここでfillableフィールドを追加する必要があります。
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 の後にモデルを保持します。
// 再度 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');
}
}
コントローラーの非再利用と再利用の違い
違いは以下の通りです。
コントローラーの非再利用
各リクエストで新しいコントローラーインスタンスを再生成し、リクエスト終了後にそのインスタンスを解放し、メモリを回収します。コントローラーを非再利用にすることは従来のフレームワークと同じであり、多くの開発者の習慣に適しています。コントローラーが繰り返し作成されて破棄されるため、性能は再利用コントローラーよりやや劣ります(helloworldの性能比較で約10%差、ビジネスロジックではほぼ無視できます)。
コントローラーの再利用
再利用の場合、1つのプロセス内でコントローラーが一度だけ生成され、リクエスト終了後はそのコントローラーインスタンスを解放しません。現在のプロセスの後続リクエストはこのインスタンスを再利用します。コントローラーの再利用は性能が向上しますが、多くの開発者の習慣には合わないかもしれません。
コントローラーの再利用ができない状況
リクエストがコントローラーのプロパティを変更する場合、コントローラーの再利用は有効にすべきではありません。なぜなら、これらのプロパティの変更が後続のリクエストに影響を及ぼすからです。
一部の開発者は、コントローラーのコンストラクタ __construct()
内で各リクエストに対して初期化を行うことを好むため、この場合はコントローラーを再利用すべきではありません。なぜなら、現在のプロセスのコンストラクタは一度だけ呼び出され、各リクエストごとに呼び出されるわけではないからです。