Контроллер
Создайте файл контроллера 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
и перезапустите.
Суффикс контроллера
С версии 1.3 webman поддерживает настройку суффикса контроллера в 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]);
}
}
Вы можете передавать значения name
и age
с помощью GET
и POST
методов, а также через параметры маршрута, например:
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)
{
// Этот метод сохранит model после первого запроса update?id=1
// Если будет снова выполнен запрос delete?id=2, то будет удалена запись 1
if (!$this->model) {
$this->model = Model::find($id);
}
return $this->model;
}
}
Подсказка
Возврат данных в конструкторе контроллера__construct()
не приведет к ожидаемым результатам, например:
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function __construct()
{
// Возврат данных в конструкторе не имеет никакого эффекта, браузер не получит этот ответ
return response('hello');
}
}
Различие между ненапряжной и повторно используемой реализацией контроллера
Различия следующие:
Ненапряжная реализация контроллера
Каждый запрос создает новый экземпляр контроллера, по окончании запроса экземпляр освобождается и память очищается. Ненапряжная реализация контроллера аналогична традиционным фреймворкам и соответствует привычкам большинства разработчиков. Поскольку контроллеры создаются и уничтожаются многократно, производительность будет немного ниже, чем при повторном использовании контроллера (производительность helloworld может быть ниже на 10%, в этом случае при наличии бизнес-логики это можно практически не учитывать).
Повторное использование контроллера
При повторном использовании один процесс создает экземпляр контроллера только один раз, а по окончании запроса экземпляр контроллера не освобождается, текущие запросы в процессе будут повторно использовать этот экземпляр. Повторное использование контроллера обеспечивает лучшую производительность, однако может не соответствовать привычкам большинства разработчиков.
Ситуации, при которых нельзя использовать повторное использование контроллера
Если запрос изменяет свойства контроллера, нельзя включать повторное использование, так как эти изменения повлияют на последующие запросы.
Некоторые разработчики предпочитают инициализировать что-либо в конструкторе контроллера __construct()
для каждого запроса, в этом случае нельзя использовать повторное использование контроллера, так как конструктор текущего процесса будет вызываться только один раз, и он не будет вызываться для каждого запроса.