Skip to content

Controladores y Peticiones

Technomantus Corvi edited this page Sep 5, 2026 · 1 revision

Controladores y peticiones

Controladores

Cada controlador extiende Controller y obtiene acceso a $this->request, además de un conjunto de helpers de respuesta.

<?php

class ArticleController extends Controller
{
    public function index(): void
    {
        $this->view('article/index', [
            'title'    => 'Artículos',
            'articles' => ArticleModel::all(),
        ]);
    }

    public function store(): void
    {
        $title = $this->request->post('title');

        if (empty($title)) {
            $this->flash('error', 'El título es obligatorio.');
            $this->redirect('/article/create');
        }

        ArticleModel::create(['title' => $title]);
        $this->flash('success', 'Artículo creado.');
        $this->redirect('/article');
    }
}

Métodos disponibles en Controller

Método Descripción
$this->view($nombre, $data) Renderiza una vista dentro del layout
$this->redirect($path) Redirige y detiene la ejecución
$this->json($data, $status) Devuelve JSON y detiene la ejecución
$this->abort404() Muestra la página 404 y detiene la ejecución
$this->flash($key, $mensaje) Guarda un mensaje flash en sesión
$this->request Instancia actual de Request

Proteger una ruta (requiere tanuki_login)

public function dashboard(): void
{
    auth_require(); // redirige a /login si no hay sesión
    $user = auth_user();

    $this->view('dashboard/index', ['user' => $user]);
}

Ver Autenticación para la referencia completa de helpers de auth.

La clase Request

$this->request->post('campo')        // Valor de $_POST
$this->request->get('param')         // Valor de $_GET
$this->request->input('campo')       // POST primero, si no GET
$this->request->all()                // Todos los inputs fusionados (POST prevalece)
$this->request->only(['a', 'b'])     // Solo esos campos
$this->request->except(['token'])    // Todos excepto esos campos
$this->request->has('campo')         // bool: ¿existe y no está vacío?
$this->request->file('avatar')       // Entrada de $_FILES, o null
$this->request->method()             // 'GET', 'POST', 'PUT'…
$this->request->isGet() / isPost() / isPut() / isDelete()
$this->request->isAjax()             // bool
$this->request->ip()                 // IP del cliente — ver nota abajo
$this->request->uri()                // Path actual, sin query string

Una nota sobre ip()

Request::ip() confía en la cabecera X-Forwarded-For, que cualquier cliente puede establecer — solo confía en ella detrás de un proxy inverso de confianza que sobrescriba esa cabecera. Ver Seguridad.

Repoblado de formularios tras un fallo de validación

public function store(): void
{
    $title       = $this->request->post('title');
    $description = $this->request->post('description', '');

    if (empty($title)) {
        keep_old(['title' => $title, 'description' => $description]);
        $this->flash('error', 'El título es obligatorio.');
        $this->redirect('/article/create');
    }
    // ...
}

En la vista:

<input type="text" name="title" value="<?= e(old('title')) ?>">

keep_old() guarda los datos en sesión; old() los recupera una vez y view() los limpia automáticamente tras renderizar, así que solo sobreviven a un único ciclo de redirect-y-mostrar-de-nuevo.

Protección CSRF

public function store(): void
{
    if (!csrf_verify($this->request->post('_token'))) {
        $this->flash('error', 'Tu sesión expiró. Inténtalo de nuevo.');
        $this->redirect('/article/create');
    }
    // ...
}

En la vista:

<form method="POST" action="/article">
    <?= csrf_field() ?>
    <!-- ... -->
</form>

No se fuerza globalmente desde el router a propósito — ver Seguridad para saber por qué.

Clone this wiki locally