Controlador

Cree un nuevo archivo 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');
    }
}

Cuando acceda a http://127.0.0.1:8787/foo, la página devuelve hello index.

Cuando acceda a http://127.0.0.1:8787/foo/hello, la página devuelve hello webman.

Por supuesto, puede cambiar las reglas de enrutamiento a través de la configuración de rutas, consulte Rutas.

Consejo
Si aparece un error 404, no se puede acceder, abra config/app.php, ajuste controller_suffix a Controller y reinicie.

Sufijo del controlador

A partir de la versión 1.3, webman admite la configuración del sufijo del controlador en config/app.php. Si controller_suffix en config/app.php está configurado como vacío '', el controlador se verá así:

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

Se recomienda encarecidamente configurar el sufijo del controlador como Controller, ya que esto evitará conflictos entre los nombres de los controladores y las clases de modelo, además de aumentar la seguridad.

Notas

  • El marco pasará automáticamente el objeto support\Request al controlador, a través del cual se pueden obtener datos de entrada del usuario (datos get, post, header, cookie, etc.), consulte Solicitudes
  • El controlador puede devolver números, cadenas o objetos support\Response, pero no puede devolver otros tipos de datos.
  • El objeto support\Response se puede crear a través de las funciones de ayudante response(), json(), xml(), jsonp(), redirect(), etc.

Vinculación de parámetros del controlador

Ejemplo

webman admite la vinculación automática de parámetros de solicitud a los parámetros del método del controlador, por ejemplo:

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

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

Puede transmitir los valores de name y age mediante GET o POST, o también puede pasar los parámetros name y age a través de parámetros de ruta, como por ejemplo:

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

La prioridad es Parámetros de ruta > GET > Parámetros POST

Valores predeterminados

Supongamos que accedemos a /user/create?name=tom, obtendremos el siguiente error:

Missing input parameter age

El motivo es que no hemos pasado el parámetro age, y podemos solucionar este problema asignando un valor predeterminado al parámetro, por ejemplo:

<?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

Cuando accedemos a /user/create?name=tom&age=not_int, obtendremos el siguiente error:

Consejo
Aquí, para facilitar la prueba, estamos introduciendo parámetros directamente en la barra de direcciones del navegador; en el desarrollo real, los parámetros deben transmitirse mediante el método POST.

Input age must be of type int, string given

Esto se debe a que los datos recibidos se convertirán según su tipo, y si no se pueden convertir, se lanzará una excepción support\exception\InputTypeException. Dado que el parámetro age proporcionado no se puede convertir al tipo int, se obtiene el error anterior.

Errores personalizados

Podemos utilizar múltiples idiomas para personalizar errores como Missing input parameter age y Input age must be of type int, string given, como se muestra en el siguiente comando:

composer require symfony/translation
mkdir resource/translations/zh_CN/ -p
echo "<?php
return [
    'Input :parameter must be of type :exceptType, :actualType given' => 'El parámetro de entrada :parameter debe ser de tipo :exceptType, se proporcionó el tipo :actualType',
    'Missing input parameter :parameter' => 'Falta el parámetro de entrada :parameter',
];" > resource/translations/zh_CN/messages.php
php start.php restart

Otros tipos

Los tipos de parámetros admitidos por webman incluyen int, float, string, bool, array, object, instancias de clase, etc., por ejemplo:

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

Cuando accedemos a /user/create?name=tom&age=18&balance=100.5&vip=true&extension[foo]=bar, obtendremos el siguiente resultado:

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

Instancias de clase

webman admite la transmisión de instancias de clase mediante la indicación de tipo de parámetro, por ejemplo:

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

Cuando accedemos a /blog/create?blog[title]=hello&blog[content]=world, obtendremos el siguiente resultado:

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

Instancias 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;
    // Aquí es necesario agregar campos que se puedan llenar para evitar la entrada de campos inseguros desde el 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;
    }
}

Cuando accedemos a /user/create?user[name]=tom&user[age]=18, obtendremos un resultado como el siguiente:

1

Ciclo de vida del controlador

Cuando controller_reuse en config/app.php está configurado como false, se inicializa una instancia del controlador correspondiente para cada solicitud, y después de que la solicitud finaliza, la instancia del controlador se destruye, lo cual es similar al mecanismo de operación de los frameworks tradicionales.

Cuando controller_reuse en config/app.php está configurado como true, todas las solicitudes reutilizarán la instancia del controlador, es decir, una vez que la instancia del controlador se crea, permanecerá en memoria y todas las solicitudes la reutilizarán.

Nota
Al activar la reutilización del controlador, las solicitudes no deben cambiar ningún atributo del controlador, porque estos cambios afectarán solicitudes futuras, por ejemplo:

<?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 retendrá model después de la primera solicitud update?id=1
        // Si se solicita nuevamente delete?id=2, se eliminarán los datos de 1
        if (!$this->model) {
            $this->model = Model::find($id);
        }
        return $this->model;
    }
}

Consejo
Los datos devueltos en el constructor __construct() del controlador no tendrán efecto, por ejemplo:

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function __construct()
    {
        // El retorno de datos dentro del constructor no tiene ningún efecto, el navegador no recibirá esta respuesta
        return response('hello'); 
    }
}

Diferencias entre no reutilizar y reutilizar controladores

Las diferencias son las siguientes:

No reutilizar el controlador

Cada solicitud creará un nuevo controlador, y el controlador se liberará y la memoria será reciclada al finalizar la solicitud. No reutilizar el controlador se asemeja a los frameworks tradicionales, y se alinea con los hábitos de la mayoría de los desarrolladores. Debido a la creación y destrucción repetida del controlador, su rendimiento será ligeramente inferior al de los controladores reutilizados (aproximadamente un 10% en las pruebas de rendimiento de helloworld, lo cual se puede ignorar en gran medida en situaciones con carga de trabajo).

Reutilizar el controlador

Si se reutiliza, una sola instancia del controlador se creará en un proceso, y esta instancia no se liberará al finalizar la solicitud. Las solicitudes posteriores del proceso actual reutilizarán esta instancia. Reutilizar el controlador ofrece un mejor rendimiento, pero no se alinea con los hábitos de muchos desarrolladores.

Las siguientes situaciones no permiten el uso de la reutilización de controladores

No se debe habilitar la reutilización del controlador cuando la solicitud modifique propiedades del controlador, ya que estos cambios afectarán a solicitudes futuras.

Algunos desarrolladores prefieren hacer inicializaciones específicas en el constructor __construct() del controlador para cada solicitud; en este caso, no se puede reutilizar el controlador, ya que el constructor solo se llamará una vez por proceso y no en cada solicitud.