Bộ điều khiển

Tạo một tệp bộ điều khiển mới 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');
    }
}

Khi truy cập http://127.0.0.1:8787/foo, trang sẽ trả về hello index.

Khi truy cập http://127.0.0.1:8787/foo/hello, trang sẽ trả về hello webman.

Tất nhiên bạn có thể thay đổi quy tắc định tuyến qua cấu hình định tuyến, tham khảo Định tuyến.

Mẹo
Nếu gặp lỗi 404 không thể truy cập, hãy mở config/app.php, đặt controller_suffixController, và khởi động lại.

Hậu tố bộ điều khiển

Webman từ phiên bản 1.3 trở đi hỗ trợ thiết lập hậu tố bộ điều khiển trong config/app.php, nếu controller_suffix trong config/app.php được thiết lập là rỗng '', thì bộ điều khiển tương tự như sau

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');
    }
}

Khuyên bạn nên đặt hậu tố bộ điều khiển là Controller, để tránh xung đột tên giữa bộ điều khiển và lớp mô hình, đồng thời tăng thêm tính bảo mật.

Giải thích

  • Khung sẽ tự động chuyển đối tượng support\Request tới bộ điều khiển, từ đó bạn có thể lấy dữ liệu đầu vào của người dùng (dữ liệu get post header cookie, v.v.), tham khảo Yêu cầu
  • Bộ điều khiển có thể trả về số, chuỗi hoặc đối tượng support\Response, nhưng không được trả về các loại dữ liệu khác.
  • Đối tượng support\Response có thể được tạo ra thông qua các hàm trợ giúp như response(), json(), xml(), jsonp(), redirect(), v.v.

Liên kết tham số bộ điều khiển

Ví dụ

Webman hỗ trợ liên kết tự động tham số yêu cầu qua tham số phương thức bộ điều khiển, ví dụ

<?php
namespace app\controller;
use support\Response;

class UserController
{
    public function create(string $name, int $age): Response
    {
        return json(['name' => $name, 'age' => $age]);
    }
}

Bạn có thể truyền giá trị của nameage qua các phương thức GET hoặc POST, hoặc có thể truyền thông qua tham số định tuyến, chẳng hạn như

Route::any('/user/{name}/{age}', [app\controller\UserController::class, 'create']);

Thứ tự ưu tiên là tham số định tuyến > GET > Tham số POST

Giá trị mặc định

Giả sử chúng ta truy cập /user/create?name=tom, chúng ta sẽ nhận được lỗi như sau

Missing input parameter age

Lý do là vì chúng ta chưa truyền tham số age, có thể giải quyết vấn đề này bằng cách thiết lập giá trị mặc định cho tham số, ví dụ

<?php
namespace app\controller;
use support\Response;

class UserController
{
    public function create(string $name, int $age = 18): Response
    {
        return json(['name' => $name, 'age' => $age]);
    }
}

Loại tham số

Khi chúng ta truy cập /user/create?name=tom&age=not_int, chúng ta sẽ nhận được lỗi như sau

Mẹo
Ở đây vì mục đích thử nghiệm, chúng ta trực tiếp nhập tham số trong thanh địa chỉ của trình duyệt, trong phát triển thực tế nên sử dụng phương thức POST để truyền tham số

Input age must be of type int, string given

Điều này xảy ra vì dữ liệu nhận được sẽ được chuyển đổi theo loại, nếu không thể chuyển đổi sẽ ném ra ngoại lệ support\exception\InputTypeException,
vì tham số age truyền vào không thể chuyển đổi sang kiểu int, nên nhận lỗi như trên.

Lỗi tùy chỉnh

Chúng ta có thể sử dụng đa ngôn ngữ để tùy chỉnh thông báo lỗi Missing input parameter ageInput age must be of type int, string given, tham khảo lệnh dưới đây

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

Các loại khác

Các loại tham số được hỗ trợ bởi webman bao gồm int, float, string, bool, array, object, khoảng xanh, v.v. ví dụ

<?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,
        ]);
    }
}

Khi chúng ta truy cập /user/create?name=tom&age=18&balance=100.5&vip=true&extension[foo]=bar, chúng ta sẽ nhận được kết quả như sau

{
  "name": "tom",
  "age": 18,
  "balance": 100.5,
  "vip": true,
  "extension": {
    "foo": "bar"
  }
}

Thực thể lớp

Webman hỗ trợ chuyển đối tượng lớp thông qua gợi ý loại tham số, ví dụ

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());
    }
}

Khi truy cập /blog/create?blog[title]=hello&blog[content]=world, chúng ta sẽ nhận được kết quả như sau

{
  "title": "hello",
  "content": "world"
}

Thực thể mô hình

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;
    // Cần thêm các trường có thể được điền, tránh trường không an toàn do phía trước gửi vào
    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;
    }
}

Khi truy cập /user/create?user[name]=tom&user[age]=18, chúng ta sẽ nhận được kết quả tương tự như sau

1

Vòng đời bộ điều khiển

Khi config/app.php thiết lập controller_reusefalse, mỗi yêu cầu sẽ khởi tạo một instance bộ điều khiển tương ứng một lần, sau khi yêu cầu kết thúc, instance bộ điều khiển sẽ bị hủy, điều này giống với cơ chế vận hành của các khung truyền thống.

Khi config/app.php thiết lập controller_reusetrue, tất cả các yêu cầu sẽ tái sử dụng instance bộ điều khiển, tức là instance bộ điều khiển một khi được tạo sẽ luôn tồn tại trong bộ nhớ, tất cả các yêu cầu sẽ tái sử dụng.

Lưu ý
Khi mở tái sử dụng bộ điều khiển, yêu cầu không nên thay đổi bất kỳ thuộc tính nào của bộ điều khiển, vì những thay đổi này sẽ ảnh hưởng đến các yêu cầu tiếp theo, ví dụ

<?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)
    {
        // Phương thức này sẽ giữ lại model sau yêu cầu đầu tiên update?id=1
        // Nếu yêu cầu lại delete?id=2 thì sẽ xóa dữ liệu của id 1
        if (!$this->model) {
            $this->model = Model::find($id);
        }
        return $this->model;
    }
}

Mẹo
Trong hàm khởi tạo của bộ điều khiển __construct() không có tác dụng gì, ví dụ

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function __construct()
    {
        // Trong hàm khởi tạo không có tác dụng return dữ liệu, trình duyệt sẽ không nhận được phản hồi này
        return response('hello'); 
    }
}

Sự khác biệt giữa việc không tái sử dụng và tái sử dụng bộ điều khiển

Sự khác biệt như sau

Không tái sử dụng bộ điều khiển

Mỗi yêu cầu sẽ tạo mới một instance bộ điều khiển, sau khi yêu cầu kết thúc, instance sẽ được giải phóng và thu hồi bộ nhớ. Không tái sử dụng bộ điều khiển giống như các khung truyền thống, phù hợp với thói quen của hầu hết các nhà phát triển. Vì bộ điều khiển được tạo và hủy liên tục nên hiệu suất sẽ kém hơn một chút so với bộ điều khiển tái sử dụng (hiệu suất đo áp lực helloworld kém hơn khoảng 10%, các trường hợp có tính thương mại có thể bỏ qua).

Tái sử dụng bộ điều khiển

Nếu tái sử dụng thì một tiến trình chỉ tạo một lần bộ điều khiển, sau khi yêu cầu kết thúc sẽ không giải phóng instance bộ điều khiển này, các yêu cầu tiếp theo trong tiến trình hiện tại sẽ tái sử dụng instance này. Tái sử dụng bộ điều khiển có hiệu suất tốt hơn nhưng không giống như thói quen của phần lớn các nhà phát triển.

Các trường hợp không thể sử dụng tái sử dụng bộ điều khiển

Khi các yêu cầu sẽ thay đổi thuộc tính của bộ điều khiển, không thể mở tái sử dụng bộ điều khiển, vì những thay đổi thuộc tính này sẽ ảnh hưởng đến các yêu cầu tiếp theo.

Một số nhà phát triển thích thực hiện một số khởi tạo cho mỗi yêu cầu trong hàm khởi tạo __construct() của bộ điều khiển, trong trường hợp này bộ điều khiển không thể được tái sử dụng, vì hàm khởi tạo chỉ được gọi một lần cho tiến trình hiện tại, không phải mỗi yêu cầu đều được gọi.