コントローラー

新しいコントローラーファイル 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_suffixController に設定し、再起動してください。

コントローラーのサフィックス

webmanは1.3バージョンから、config/app.php でコントローラーのサフィックスを設定することができ、config/app.phpcontroller_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 メソッドを使って nameage の値を渡すことができて、ルーティングパラメーター経由でも nameage パラメーターを渡すことができます。例えば:

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 ageInput 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がサポートするパラメーター型には、intfloatstringboolarrayobjectクラスインスタンス などがあります。例えば:

<?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.phpcontroller_reusefalse の場合、各リクエストごとに対応するコントローラーインスタンスが初期化され、リクエスト終了後にコントローラーインスタンスが破棄されます。これは従来のフレームワークの動作メカニズムと同じです。

config/app.phpcontroller_reusetrue の場合、すべてのリクエストはコントローラーインスタンスを再利用します。つまり、コントローラーインスタンスは一度作成されるとメモリに常駐し、すべてのリクエストで再利用されます。

注意
コントローラー再利用を有効にする場合、リクエストはコントローラーのいかなるプロパティも変更すべきではありません。これらの変更は後続のリクエストに影響を及ぼすためです。例えば:

<?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() 内で各リクエストに対して初期化を行うことを好むため、この場合はコントローラーを再利用すべきではありません。なぜなら、現在のプロセスのコンストラクタは一度だけ呼び出され、各リクエストごとに呼び出されるわけではないからです。