Hace tiempo hice un vídeo acerca de la nueva forma de desarrollar aplicaciones web con Laravel Livewire, lo puedes ver aquí. En esta entrada vamos a ver cómo trabajar con formularios en Laravel Livewire utilizando el sistema de validación de Laravel para crear y actualizar registros.
Curso recomendado con Laravel 9: Desarrolla un formulario con subida de archivos en Laravel Livewire
Curso recomendado con Laravel 9: Formularios Wizard en Laravel
Curso recomendado con Laravel 9: Formularios en Laravel, aprende a validar formularios complejos
Preparar la base de datos
Vamos a trabajar con un modelo Service, el cual será el encargado de gestionar servicios que nosotros como desarrolladores web podamos ofrecer a nuestros clientes: desarrollo web, gestión de servidores etc.
Para ello, lo primero será crear una migración, un modelo y un controlador.
php artisan make:model Service -mc<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateServicesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('services', function (Blueprint $table) {
$table->id();
$table->foreignId("user_id")->constrained();
$table->string("name", 100);
$table->float("default_hourly_rate_per_service")->comment("Precio hora por defecto para este proyecto");
$table->text("comments")->nullable()->comment("Notas adicionales de interés");
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('services');
}
}<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Service extends Model
{
use HasFactory;
protected $fillable = [
"user_id", "name", "comments", "default_hourly_rate_per_service",
];
}<?php
namespace App\Http\Controllers;
use App\Models\Service;
class ServiceController extends Controller
{
/**
* Show the form for creating a new resource.
* @return Renderable
*/
public function create()
{
return view('services.create', [
"service" => new Service,
"title" => __("Crear nuevo Servicio"),
"formName" => __("Formulario de servicios"),
"textButton" => __("Guardar"),
]);
}
/**
* Show the form for editing the specified resource.
* @param int $id
* @return Renderable
*/
public function edit(int $id)
{
return view('services.edit', [
"service" => Service::find($id),
"title" => __("Editar Servicio"),
"formName" => __("Formulario de servicios"),
"textButton" => __("Actualizar"),
]);
}
}Componente Formulario con Livewire
Ahora vamos a ejecutar el siguiente comando para crear un componente que represente un formulario con Livewire.
php artisan make:livewire Service/FormAhora, vamos a modificar ese componente con el siguiente código.
<?php
namespace App\Http\Livewire\Service;
use Livewire\Component;
use Livewire\Redirector;
use App\Models\Service;
class Form extends Component
{
public Service $service;
public $textButton;
public $updating;
public function rules(): array {
if (!$this->updating) {
return [
'service.name' => 'required|string|min:6',
'service.default_hourly_rate_per_service' => 'required|numeric',
'service.comments' => 'nullable|string',
];
}
return [
'service.name' => 'required|string|min:6',
'service.default_hourly_rate_per_service' => 'required|numeric',
'service.comments' => 'nullable|string',
];
}
public function render()
{
return view('livewire.service.form');
}
public function store(): Redirector
{
$this->validate();
$this->service->user_id = auth()->id();
$this->service->save();
session()->flash('notification', ['type' => 'success', 'title' => __("Servicio creado"), 'message' => __("El Servicio ha sido creado correctamente")]);
return redirect()->to(route("services.index"));
}
public function update(): Redirector
{
$this->validate();
$this->service->save();
session()->flash('notification', ['type' => 'success', 'title' => __("Servicio actualizado"), 'message' => __("El Servicio ha sido actualizado correctamente")]);
return redirect()->to(route("services.index"));
}
}Las propiedades de ámbito público en Livewire están disponibles de forma automática en la template.
Rules son las reglas de validación de nuestro componente, más abajo con $this->validate() se utilizan para validar el formulario.
Para validar las propiedades del objeto Service sólo debemos definir la validación con service.{prop}.
El método store será utilizado para crear nuevos servicios.
El método update será utilizado para actualizar servicios existentes.
Pintar formularios en Laravel Livewire con Laravel Collective
Ahora abre el archivo form.blade.php y modifica su código por el siguiente. En mi caso, estoy trabajando con Laravel Collective, así que el siguiente código será suficiente para el formulario de alta/edición.
<div>
<form wire:submit.prevent="{{ $updating ? 'update' : 'store' }}">
<div class="grid grid-cols-12 gap-2 mt-3">
<div class="col-span-6">
<label for="name" class="form-label w-full flex flex-col sm:flex-row">
{{ __("Nombre") }} <span class="sm:ml-auto mt-1 sm:mt-0 text-xs text-gray-600">{{ __("Requerido") }}</span>
</label>
{!! FormFacade::input('text', 'name', old('name'), ["wire:model" => "service.name", 'class' => 'form-control' . ($errors->has('name') ? ' border-theme-21' : null), 'id' => 'name', 'minlength' => 2, 'required' => true]) !!}
@error('service.name')
<div class="text-theme-21 mt-2">
{{ $errors->first('service.name') }}
</div>
@enderror
</div>
<div class="col-span-6">
<label for="default_hourly_rate_per_service" class="form-label w-full flex flex-col sm:flex-row">
{{ __("Precio hora por defecto para este servicio") }} <span class="sm:ml-auto mt-1 sm:mt-0 text-xs text-gray-600">{{ __("Requerido") }}</span>
</label>
{!! FormFacade::input('text', 'default_hourly_rate_per_serviceme', old('default_hourly_rate_per_service'), ["wire:model" => "service.default_hourly_rate_per_service", 'class' => 'form-control' . ($errors->has('default_hourly_rate_per_service') ? ' border-theme-21' : null), 'id' => 'default_hourly_rate_per_service', 'minlength' => 1, 'required' => true]) !!}
@error('service.default_hourly_rate_per_service')
<div class="text-theme-21 mt-2">
{{ $errors->first('service.default_hourly_rate_per_service') }}
</div>
@enderror
</div>
</div>
<div class="input-form mt-3">
<label for="comments" class="form-label w-full flex flex-col sm:flex-row">
{{ __("Comentarios") }} <span class="sm:ml-auto mt-1 sm:mt-0 text-xs text-gray-600">{{ __("Cualquier nota de interés") }}</span>
</label>
{!! FormFacade::textarea('comments', old('comments'), ['wire:model' => 'service.comments', 'class' => 'form-control' . ($errors->has('comments') ? ' border-theme-21' : null), 'id' => 'comments', 'placeholder' => __("El servicio de móvil web es más complejo"), 'minlength' => 10]) !!}
@error('service.comments')
<div class="text-theme-24 mt-2">
{{ $errors->first('service.comments') }}
</div>
@enderror
</div>
{!! FormFacade::submit($textButton, ["class" => "btn btn-primary mt-5"]) !!}
</form>
</div>El formulario de servicios en sí es sencillo, simplemente hace uso de la directiva wire:submit y wire:model para que nuestro formulario se pueda ejecutar contra nuestro componente Livewire aplicando las reglas de validación de forma correcta.
Para finalizar, sólo debes actualizar las vistas del controlador ServiceController para que el método create y edit muestren el componente formulario de Livewire que acabamos de crear.
create.blade.php
<livewire:service.form :text-button="$textButton" :service="$service" />edit.blade.php
<livewire:service.form :text-button="$textButton" :service="$service" :updating="true" />
Curso Laravel 12
Completo 2026
El único curso 100% actualizado que incluye Laravel 12, Livewire 3, Vue 3, React 19 e Inertia 2. Aprende con proyectos reales y las últimas funcionalidades.
star Incluido en cualquier suscripción