Tutorial: Inertia.js + Laravel – Obtención y guardado de datos

 


Introducción

Inertia.js es un puente entre tu backend (Laravel) y tu frontend (Vue/React/Svelte) que te permite construir aplicaciones de una sola página (SPA) sin necesidad de crear una API REST ni usar un enrutador del lado del cliente. En lugar de eso, usas las rutas y controladores de Laravel como siempre, pero Inertia se encarga de actualizar la vista (el componente Vue) sin recargar la página completa.

Ventajas principales:

  • No necesitas construir una API separada.

  • Mantienes el flujo tradicional de Laravel (rutas, controladores, sesiones, validación).

  • El frontend se beneficia de la reactividad de Vue sin la complejidad de un SPA puro.

  • Las redirecciones y validaciones funcionan de forma nativa.

En este tutorial, nos basamos en el ejemplo de una lista de tareas guardada en sesión. Veremos:

  • Cómo obtener datos (desde el controlador a la vista).

  • Cómo guardar datos (desde el formulario al controlador).

  • Comparativa con el enfoque tradicional (sin Inertia) para entender las diferencias.


1. Estructura de archivos del ejemplo

Los archivos que vamos a tocar son:

text
routes/web.php                    → Definición de rutas.
app/Http/Controllers/TareaController.php → Lógica del controlador.
resources/js/Pages/Tarea.vue       → Componente Vue (la vista).

Además, asumimos que tienes un proyecto Laravel con Inertia y Vue 3 instalado (laravel new proyecto --inertia=vue).


2. Obtención de datos (Mostrar la tarea)

Enfoque tradicional (sin Inertia)

En una aplicación Laravel clásica con Blade, para mostrar datos harías algo así:

Controlador:

php
public function index()
{
    $tarea = session('tarea', '');
    return view('tarea', compact('tarea'));
}

Vista Blade (tarea.blade.php):

blade
<h1>Mi Tarea</h1>
<form method="POST" action="/tarea">
    @csrf
    <input type="text" name="tarea" value="{{ old('tarea') }}">
    <button type="submit">Guardar</button>
</form>
@if ($tarea)
    <div><strong>Tarea actual:</strong> {{ $tarea }}</div>
@else
    <p>No hay tarea guardada.</p>
@endif
  • El controlador devuelve una vista Blade con los datos incrustados en el HTML.

  • La página se renderiza completamente en el servidor y se envía al cliente.

Enfoque con Inertia

Controlador (index):

php
public function index()
{
    $tarea = session('tarea', '');
    return Inertia::render('Tarea', [
        'tarea' => $tarea
    ]);
}

Componente Vue (Tarea.vue):

vue
<template>
  <h1>Mi Tarea</h1>
  <!-- Formulario con Inertia -->
  <form @submit.prevent="guardarTarea">
    <input type="text" v-model="form.tarea" placeholder="Escribe una tarea..." />
    <button type="submit">Guardar</button>
  </form>
  <div v-if="tarea"><strong>Tarea actual:</strong> {{ tarea }}</div>
  <p v-else>No hay tarea guardada.</p>
</template>

<script setup>
import { useForm } from '@inertiajs/vue3';

defineProps({
  tarea: String
});

const form = useForm({ tarea: '' });

function guardarTarea() {
  form.post('/tarea', {
    preserveState: true,
    onSuccess: () => { form.tarea = ''; }
  });
}
</script>

Diferencias clave en la obtención de datos:

  1. En lugar de view(), usamos Inertia::render() que recibe el nombre del componente Vue y un array de propiedades (props) que se pasarán al componente.

  2. El componente Vue declara las props con defineProps para recibir los datos del servidor.

  3. Inertia serializa las props a JSON y las envía al cliente junto con el componente. Luego, el cliente renderiza el componente con esos datos.

  4. No hay recarga de página: al navegar a una ruta, Inertia intercepta la respuesta y solo actualiza el contenido de la página (el componente) sin recargar el navegador.

Conclusión: La obtención de datos es similar en cuanto a que el controlador obtiene los datos y los pasa a la vista. La diferencia está en que en lugar de una vista Blade, se pasa un componente Vue con props, y la comunicación es mediante JSON en lugar de HTML.


3. Guardado de datos (Enviar tarea)

Enfoque tradicional (sin Inertia)

Controlador (store):

php
public function store(Request $request)
{
    $request->validate(['tarea' => 'required|string|max:255']);
    session(['tarea' => $request->tarea]);
    return redirect()->route('tarea.index')->with('success', 'Tarea guardada');
}
  • El formulario envía una petición POST tradicional.

  • El controlador valida, guarda en sesión y redirige a la ruta index.

  • La redirección hace que el navegador recargue la página y se muestre la nueva tarea.

En la vista Blade, el formulario usa @csrf y method="POST"; el envío provoca una recarga completa.

Enfoque con Inertia

Controlador (store):

php
public function store(Request $request)
{
    $request->validate(['tarea' => 'required|string|max:255']);
    session(['tarea' => $request->tarea]);
    return redirect()->route('tarea.index');
}

Componente Vue (Tarea.vue):

vue
<form @submit.prevent="guardarTarea">
  <input type="text" v-model="form.tarea" />
  <button type="submit">Guardar</button>
</form>

<script setup>
import { useForm } from '@inertiajs/vue3';
const form = useForm({ tarea: '' });

function guardarTarea() {
  form.post('/tarea', {
    preserveState: true,
    onSuccess: () => { form.tarea = ''; }
  });
}
</script>

Diferencias clave en el guardado:

  1. No usamos @csrf manualmente: Inertia incluye automáticamente el token CSRF en las peticiones.

  2. En lugar de un envío de formulario HTML tradicional, usamos form.post() (o form.put, form.delete, etc.) que es un método de Inertia que envía una petición POST mediante fetch (AJAX).

  3. No hay recarga de página: Inertia envía la petición, el servidor responde con una redirección (302) y Inertia sigue esa redirección (hace una petición GET al nuevo destino) y actualiza la página con la nueva respuesta. Todo esto ocurre sin recargar el navegador.

  4. Manejo de estado: preserveState: true mantiene el valor del input después del envío (útil para que no se borre inmediatamente). En el onSuccess lo limpiamos manualmente.

  5. Validación: Si la validación falla, Inertia devuelve automáticamente los errores en form.errors, y puedes mostrarlos en el componente.

Conclusión: En el enfoque tradicional, el guardado implica un viaje completo (POST → redirección → GET → renderizado completo). En Inertia, es una petición AJAX que actualiza la vista sin recarga, pero el controlador sigue siendo el mismo (validación, persistencia, redirección). La redirección es clave porque Inertia la interpreta como una nueva visita a esa URL.


4. Explicación detallada del flujo en Inertia

Ciclo completo con Inertia

  1. El usuario entra a /tarea (GET).

    • El controlador index() obtiene la tarea de la sesión y llama a Inertia::render('Tarea', ['tarea' => $tarea]).

    • Inertia genera una respuesta con un JSON especial que incluye el nombre del componente y las props.

    • El cliente (Vue) recibe ese JSON y renderiza el componente Tarea.vue con las props.

  2. El usuario escribe y envía el formulario.

    • Se ejecuta guardarTarea() que llama a form.post('/tarea', ...).

    • Inertia realiza una petición POST a /tarea (con los datos del formulario).

    • El controlador store() valida, guarda en sesión y retorna redirect()->route('tarea.index').

  3. Inertia maneja la redirección.

    • Inertia detecta que la respuesta es una redirección (código 302).

    • Automáticamente, Inertia hace una petición GET a la URL de la redirección (/tarea) para obtener la nueva página.

    • El controlador index() se ejecuta de nuevo, obtiene la tarea actualizada y devuelve el componente con las nuevas props.

    • Inertia actualiza el componente en el DOM sin recargar la página.

¿Qué pasa con el estado del formulario?

  • useForm crea un objeto reactivo con los campos del formulario y además mantiene estado de envío (processing), errores (errors), etc.

  • Al hacer form.post, el formulario se envía y se pone en estado de "procesando" (form.processing es true).

  • Cuando la petición se completa, se restaura el estado. Si hay errores de validación, form.errors se llena y puedes mostrarlos.

  • preserveState: true evita que el input se reinicie a su valor inicial después del envío (útil para mostrar el valor enviado). En el ejemplo, lo limpiamos en onSuccess.


5. Comparativa detallada: Tradicional vs Inertia

AspectoTradicional (Blade)Inertia (Vue)
RenderizadoServidor: genera HTML completo.Servidor: envía datos JSON + nombre del componente. Cliente: renderiza el componente.
NavegaciónCada enlace o redirección recarga la página.Inertia intercepta los enlaces y redirecciones, hace peticiones AJAX y actualiza solo el contenido.
Envío de formulariosEnvío tradicional (sincrónico) con recarga.Envío asíncrono (fetch) sin recarga.
CSRFSe requiere @csrf en el formulario.Inertia lo maneja automáticamente.
ValidaciónLos errores se pasan a la vista con withErrors() o session('errors').Los errores se asignan a form.errors y se pueden mostrar reactivamente.
RedireccionesRedirección HTTP que fuerza una nueva carga de página.Inertia sigue la redirección y actualiza la página sin recarga.
Manejo de estadoSe usa old() para mantener valores después de errores.useForm mantiene el estado del formulario de manera reactiva.
InteractividadSe necesita JavaScript adicional (AJAX) para interactividad sin recarga.La interactividad es nativa gracias a Vue y la reactividad.
Complejidad inicialMenos curva de aprendizaje (Blade es simple).Curva de aprendizaje mayor (Vue + Inertia).
MantenibilidadPuede volverse complejo con muchas interacciones (jQuery, etc.).Más estructurado y mantenible para SPAs.

6. Ventajas de Inertia para obtener y guardar datos

  • Sin API: No necesitas crear endpoints REST. Usas los mismos controladores para renderizar y procesar.

  • Reutilización de lógica: La validación y la persistencia son las mismas que en Laravel tradicional.

  • Mejor experiencia de usuario: Navegación sin recargas, transiciones suaves.

  • Estado compartido: Las props se pasan del servidor al cliente de manera sencilla, sin tener que hacer llamadas AJAX adicionales.

  • Manejo de errores integrado: Los errores de validación se manejan automáticamente.


7. Profundizando en el ejemplo

¿Por qué usamos sesión en lugar de base de datos?

Para mantener el ejemplo mínimo y funcional. En una aplicación real, en lugar de session(['tarea' => ...]), usarías un modelo Eloquent:

php
$tarea = new Tarea();
$tarea->descripcion = $request->tarea;
$tarea->save();

Y en el index():

php
$tareas = Tarea::all(); // o alguna consulta
return Inertia::render('Tarea', ['tareas' => $tareas]);

El flujo sería el mismo, solo cambia la persistencia.

Mostrar errores de validación

En el componente Vue, puedes añadir:

vue
<div v-if="form.errors.tarea" style="color: red;">
  {{ form.errors.tarea }}
</div>

Navegación con Inertia

En lugar de <a href="/otra">, usas <Link href="/otra"> (importado de @inertia/vue3) para que la navegación sea manejada por Inertia sin recarga.


8. Conclusión

Inertia.js ofrece un enfoque moderno para construir aplicaciones web con Laravel y Vue, manteniendo la simplicidad del backend de Laravel y añadiendo la reactividad de Vue. La obtención de datos se hace mediante props que el controlador pasa al componente, y el guardado se realiza con peticiones asíncronas que no recargan la página, pero que siguen el flujo tradicional de redirección y validación.

Comparado con el enfoque clásico de Blade, Inertia reduce la cantidad de JavaScript necesario para lograr una SPA, elimina la necesidad de construir una API y ofrece una experiencia de desarrollo más cohesionada.


9. Recursos adicionales

  • Documentación oficial de Inertia

  • Documentación de Laravel con Inertia

  • Repositorio del ejemplo mínimo


Espero que este tutorial te haya aclarado cómo obtener y guardar datos con Inertia en Laravel, y cómo se diferencia del enfoque tradicional. ¡Anímate a probarlo!

Comentarios

Entradas más populares de este blog

Tareas con Laravel + Inertia + Jetstream

HACER CAMBIOS-AL EJEMPLO POR DEFECTO

Laravel + Inertia - ejemplo