Saltar a contenido

CRUD y formularios

Los formularios es la manera que tiene el usuario de interactuar con la aplicación, de ahí su especial importancia y relevancia. En Laravel no iba a ser menos, y para ello usaremos una serie de elementos que nos permitan recoger la información para poder tratarla.

La vista

A la hora de crear un formulario debemos crear una vista que sea la que "pinte" esa parte de nuestra web. Es por ello que debemos crear la nuestra en el directorio resources\views.

Respeta las rutas

Aunque no es obligatorio, es muy recomendable que tanto vistas como controladores y modelos respeten la estructura de directorios. Si por ejemplo tenemos una carpeta backendcon todos los controladores de la parte de la administración, es recomendable que tengamos un directorio backend con todas las vistas de esta parte. Esto se hace por coherencia.

Como no se ha mostrado hasta ahora el uso de plantillas maestras, lo vamos a hacer ahora con el formulario. Esto es especialmente útil cuando queremos reaprovechar partes de la vista.

master.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Dashboard</title>
</head>
<body>
    {{-- <header>
        Header 2
    </header> --}}

    @yield('content')

   <section>
        @yield('morecontent')
   </section>

</body>
</html>

Supongamos que tenemos nuestro formulario dentro de resources\views\backend. Por lo que nuestro formulario de creación de post sería:

backend\create.blade.php
@extends('master')

@section('content')
   <form action="{{ route('post.store') }}" method="post">
        @csrf

        <label for="">Title</label>
        <input type="text" name="title">

        <label for="">Slug</label>
        <input type="text" name="slug">

        <label for="">Content</label>
        <textarea name="content"></textarea>

        <label for="">Description</label>
        <textarea name="description"></textarea>

        <label for="">Posted</label>
        <select name="posted">
            <option selected value="not">Not</option>
            <option value="yes">Yes</option>
        </select>

        <button type="submit">Send</button>
   </form>
@endsection

CSRF

De manera interna, Laravel tiene una protección de ataques csrf, por lo que debemos implementar en nuestros formularios la directiva @csrf que genera un token único para comprobar que el formulario es realmente de nuestro sitio web. Este token varía, no es estático.

El controlador

El controlador del post, de momento nos quedaría así:

backend\PostController
<?php
namespace App\Http\Controllers\Backend;

use App\Http\Controllers\Controller;
use App\Http\Requests\Post\PutRequest;
use App\Http\Requests\Post\StoreRequest;
use Illuminate\Http\Request;

use App\Models\Post;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        return view('backend/index');
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        $post = new Post();

        return view('dashboard.post.create', compact('post'));
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(StoreRequest $request)
    {
        Post::create($request->all());
        return to_route('post.index');
    }

}

Los controladores tienen funciones específicas para realizar acciones:

  • create(): es la que nos mostrará el formulario para poder crear nuestro recurso.
  • store($request): es lo que se recoge del formulario de creación y que normalmente se usa para crear el recurso con los datos recogidos. Siendo $request los valores de los diferentes campos del formulario.

Validaciones

Cuando se trabaja con datos que han de ser introducidos por el usuario, la validación de los mismos es vital para la integridad y seguridad de nuestra aplicación. Esto es algo que vimos en la UT 04 y que Laravel va a gestionar por nosotros. Algunas de las validaciones se pueden hacer en diferentes sitios (en lenguaje de marcado o en el cliente), nosotros vamos a ver lo que podemos hacer en el servidor.

En el controlador

En el controlador es el lugar adecuado para realizar la validación de los datos antes de ejecutar acciones como crear, actualizar o eliminar registros. Laravel permite validar datos de dos formas principales, y ambas se utilizan justo antes de persistir información en la base de datos.

Validación directa con el objeto Request

Este método es simple y claro. Utiliza el objeto Request directamente para validar los datos entrantes mediante el método validate(). Si la validación falla, Laravel redirige automáticamente de vuelta a la vista anterior, manteniendo los datos y mostrando los errores.

backend\PostController
<?php

public function store(StoreRequest $request)
{
    // Si no realiza la validación realiza un redireccion a la ruta de la que venía
    $request->validate([
        'title' => 'required|min:5|max:500',
        'slug' => 'required|min:5|max:500',
        'content' => 'required|min:7',
        'description' => 'required|min:7',
        'posted' => 'required',
    ]);

    Post::create($request->all());
    return to_route('post.index');
}

Validación manual con el facade Validator

Este segundo método utiliza el facade Validator, una clase estática que permite crear y gestionar validaciones de forma más controlada y personalizada. Aunque actualmente su uso es menos común en comparación con el anterior, sigue siendo útil cuando se necesita mayor flexibilidad, como por ejemplo, manejar los errores manualmente o realizar validaciones condicionales.

backend\PostController
<?php

namespace App\Http\Controllers\Backend;

use App\Http\Controllers\Controller;
use App\Http\Requests\Post\PutRequest;
use App\Http\Requests\Post\StoreRequest;
use Illuminate\Http\Request;

use App\Models\Post;
use Illuminate\Support\Facades\Validator;

class PostController extends Controller
{

    public function store(StoreRequest $request)
    {
        // Devuelve un resultado no como en el caso anterior

        $validated = Validator::make($request->all(),
        [
            'title' => 'required|min:5|max:500',
            'slug' => 'required|min:5|max:500',
            'content' => 'required|min:7',
            'description' => 'required|min:7',
            'posted' => 'required',
        ]
        );
    }
}

Ambos métodos son válidos. El primero es más directo y legible, ideal para casos estándar. El segundo ofrece un mayor control, útil en validaciones complejas o personalizadas.

Mediante FormRequest

Este es el esquema que generalmente empleamos ya que nos permite tener todas las reglas de validación en un archivo aparte. Para generar esta clase, tenemos que usar un comando:

php artisan make:request ‹NombreRequest>
En donde NombreRequest es el nombre del archivo y clase. También podemos guardarlos en una carpeta, tal y como ocurre con los controladores:

php artisan make:request Post/StoreRequest

Y tendremos un nuevo archivo en App/Http/Requests/Post/StoreRequest.php y que debemos rellenar con la validación. En nuestro caso:

<?php

namespace App\Http\Requests\Post;

use Illuminate\Foundation\Http\FormRequest;

class StoreRequest extends FormRequest
{
    /**
     * Determina si el usuario está autorizado para realizar esta petición.
     * 
     * En este caso, devolvemos true para permitir el acceso sin restricciones.
     * Si quisiéramos restringir el acceso a usuarios autenticados o con roles,
     * deberíamos incluir la lógica aquí.
     */
    public function authorize(): bool
    {
        return true; 
    }

    /**
     * Define las reglas de validación que se aplican a esta petición.
     * 
     * Estas reglas se ejecutan automáticamente cuando usamos este Request
     * como tipo de parámetro en un controlador (inyección automática).
     * 
     * Cada campo del formulario debe cumplir las condiciones especificadas.
     *
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'title' => 'required|min:5|max:500',
            'slug' => 'required|min:3|max:500|unique:posts',
            'content' => 'required|min:7',
            'description' => 'required|min:7',
            'posted' => 'required',
        ];
    }
}

Nuestra función de store se modifica cambiando el tipo de $requesta StoreRequest. Además, debemos usar la función validated():

backend\PostController
<?php

public function store(StoreRequest $request)
{
    // Si no realiza la validación realiza un redireccion a la ruta de la que venía 
    Post::create($request->validated());
    return to_route('post.index');
}

Mostrar errores en el formulario

Laravel acumula los errores en una variable del sistema $errors. De este modo, lo podremos utilizar sin tener que preguntarnos si hay errores o no. Para ello vamos a modificar nuestra vista:

backend\create.blade.php
@extends('master')

@section('content')
    @if ($errors->any())
        @foreach ($errors->all() as $e)
            <div>
                {{ $e }}
            </div>
        @endforeach
    @endif

   <form action="{{ route('post.store') }}" method="post">
        @csrf

        <label for="">Title</label>
        <input type="text" name="title">

        <label for="">Slug</label>
        <input type="text" name="slug">

        <label for="">Content</label>
        <textarea name="content"></textarea>

        <label for="">Description</label>
        <textarea name="description"></textarea>

        <label for="">Posted</label>
        <select name="posted">
            <option selected value="not">Not</option>
            <option value="yes">Yes</option>
        </select>

        <button type="submit">Send</button>
   </form>
@endsection

Usa parciales

Como he comentado en la vista maestra, podemos usar partes parciales. De este modo, tendremos una serie de ficheros que pueden ser importados en diferentes plantillas. Un ejemplo sería la plantilla create.blaze.php que podría quedar así:

@extends('dashboard.master')

@section('content')

    @include('dashboard.fragment._errors-form')

<form action="{{ route('post.store') }}" method="post">
    @include('dashboard.post._form')
</form>
@endsection

Actividades

  • 📝 AC 721. (RA8 / CE8d CE8e / IC1 / 3p) - Realiza un formulario que te permita añadir nuevas tareas.

  • ⚓ AR 722. (RA8 / CE8d CE8e / IC1 / 3p) - Gestiona la creación y edición de categorías y post.

  • 🧪 PR 723. (RA8 / CE8d CE8e / IC2 / 5p) - El CTO ha recibido el siguiente mensaje del jefe de producción: "Necesito ser yo el que meta los productos, además el encargado de SEO quiere poder retocar los datos de los productos".