Controlador
Crie um novo arquivo de controlador 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');
}
}
Quando acessar http://127.0.0.1:8787/foo
, a página retornará hello index
.
Quando acessar http://127.0.0.1:8787/foo/hello
, a página retornará hello webman
.
É claro que você pode alterar as regras de roteamento através da configuração de roteamento, consulte Roteamento.
Dica
Se você encontrar um erro 404 e não conseguir acessar, abraconfig/app.php
, definacontroller_suffix
comoController
e reinicie.
Sufixo do Controlador
O webman a partir da versão 1.3 suporta a configuração de sufixo de controlador em config/app.php
. Se controller_suffix
em config/app.php
estiver definido como vazio ''
, o controlador será semelhante ao seguinte
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');
}
}
É altamente recomendável definir o sufixo do controlador como Controller
, pois isso pode evitar conflitos de nomes entre controladores e modelos, além de aumentar a segurança.
Nota
- O framework irá automaticamente passar o objeto
support\Request
ao controlador, através do qual você pode obter os dados de entrada do usuário (get, post, cabeçalho, cookie, etc.), consulte Requisição - O controlador pode retornar números, strings ou objetos
support\Response
, mas não pode retornar outros tipos de dados. - O objeto
support\Response
pode ser criado através das funções auxiliaresresponse()
,json()
,xml()
,jsonp()
,redirect()
, etc.
Vinculação de Parâmetros do Controlador
Exemplo
O webman suporta a vinculação automática de parâmetros de requisição através dos parâmetros de método do controlador, por exemplo
<?php
namespace app\controller;
use support\Response;
class UserController
{
public function create(string $name, int $age): Response
{
return json(['name' => $name, 'age' => $age]);
}
}
Você pode passar os valores de name
e age
através dos métodos GET
ou POST
, ou passar os parâmetros name
e age
através dos parâmetros de rota, por exemplo
Route::any('/user/{name}/{age}', [app\controller\UserController::class, 'create']);
A prioridade é parâmetros de rota
> GET
> parâmetros POST
Valores padrão
Suponha que acessamos /user/create?name=tom
, obteremos o seguinte erro
Missing input parameter age
A razão é que não passamos o parâmetro age
. Podemos resolver esse problema definindo um valor padrão para o parâmetro, por exemplo
<?php
namespace app\controller;
use support\Response;
class UserController
{
public function create(string $name, int $age = 18): Response
{
return json(['name' => $name, 'age' => $age]);
}
}
Tipo de Parâmetro
Quando acessamos /user/create?name=tom&age=not_int
, obteremos o seguinte erro
Dica
Aqui, para facilitar os testes, inserimos diretamente os parâmetros na barra de endereço do navegador. Na prática de desenvolvimento, os parâmetros devem ser passados via métodoPOST
.
Input age must be of type int, string given
Isso ocorre porque os dados recebidos serão convertidos conforme o tipo. Se a conversão não for possível, será lançada uma exceção support\exception\InputTypeException
, pois o parâmetro age
passado não pode ser convertido para o tipo int
, resultando no erro acima.
Erros Personalizados
Podemos utilizar o suporte a múltiplos idiomas para personalizar erros como Missing input parameter age
e Input age must be of type int, string given
, consulte o comando a seguir
composer require symfony/translation
mkdir resource/translations/zh_CN/ -p
echo "<?php
return [
'Input :parameter must be of type :exceptType, :actualType given' => 'O parâmetro de entrada :parameter deve ser do tipo :exceptType, foi passado o tipo :actualType',
'Missing input parameter :parameter' => 'Parâmetro de entrada :parameter ausente',
];" > resource/translations/zh_CN/messages.php
php start.php restart
Outros Tipos
Os tipos de parâmetro suportados pelo webman incluem int
, float
, string
, bool
, array
, object
, instância de classe
, etc., por exemplo
<?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,
]);
}
}
Quando acessamos /user/create?name=tom&age=18&balance=100.5&vip=true&extension[foo]=bar
, obteremos o seguinte resultado
{
"name": "tom",
"age": 18,
"balance": 100.5,
"vip": true,
"extension": {
"foo": "bar"
}
}
Instância de Classe
O webman suporta a passagem de instâncias de classe através de sugestões de tipo de parâmetro, por exemplo
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());
}
}
Quando acessamos /blog/create?blog[title]=hello&blog[content]=world
, obteremos o seguinte resultado
{
"title": "hello",
"content": "world"
}
Instância de Modelo
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;
// Aqui é necessário adicionar os campos preenchíveis, para evitar que campos inseguros sejam passados do frontend
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;
}
}
Quando acessamos /user/create?user[name]=tom&user[age]=18
, obteremos um resultado semelhante a este
1
Ciclo de Vida do Controlador
Quando controller_reuse
em config/app.php
estiver definido como false
, uma nova instância do controlador correspondente será inicializada para cada requisição. Após o término da requisição, a instância do controlador será destruída, o que é semelhante ao mecanismo de execução dos frameworks tradicionais.
Quando controller_reuse
em config/app.php
estiver definido como true
, todas as requisições reutilizarão a instância do controlador, ou seja, uma vez que a instância do controlador é criada, ela permanece na memória, e todas as requisições reutilizarão essa instância.
Nota
Ao habilitar a reutilização do controlador, a requisição não deve alterar nenhuma propriedade do controlador, pois essas alterações afetarão as requisições subsequentes. Por exemplo
<?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)
{
// Este método irá reter o model após a primeira requisição update?id=1
// Se uma nova requisição delete?id=2 for feita, deletará os dados de 1
if (!$this->model) {
$this->model = Model::find($id);
}
return $this->model;
}
}
Dica
Retornar dados na função construtora__construct()
do controlador não terá efeito, por exemplo
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function __construct()
{
// Retornar dados na função construtora não terá efeito, o navegador não receberá essa resposta
return response('hello');
}
}
Diferença entre Não Reutilizar e Reutilizar Controlador
As diferenças são as seguintes
Não Reutilizar Controlador
Cada requisição cria uma nova instância do controlador. Após a requisição término, essa instância é liberada e a memória é recuperada. O não reuso do controlador é semelhante aos frameworks tradicionais, o que é mais familiar para a maioria dos desenvolvedores. Devido à criação e destruição repetidas do controlador, o desempenho será ligeiramente inferior ao de controladores reutilizados (a diferença de desempenho no teste de carga "helloworld" é de cerca de 10%, em carga real pode ser basicamente ignorada).
Reutilizar Controlador
No caso de reutilização, um processo cria a instância do controlador apenas uma vez. Após o término da requisição, a instância do controlador não é liberada e será reutilizada nas requisições subsequentes desse processo. O desempenho do controlador reutilizado é melhor, mas não é o que a maioria dos desenvolvedores está acostumada.
Caso em que não se pode usar Reutilização do Controlador
Quando uma requisição altera as propriedades do controlador, a reutilização do controlador não deve ser ativada, pois essas mudanças afetarão as requisições subsequentes.
Alguns desenvolvedores gostam de fazer algumas inicializações para cada requisição no construtor __construct()
do controlador, nesse caso, a reutilização do controlador não deve ser usada, pois o construtor do processo atual será chamado apenas uma vez, e não a cada requisição.