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, abra config/app.php, defina controller_suffix como Controller 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 auxiliares response(), 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étodo POST.

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.