galax13a / live4crud-tailwind
Scaffold Livewire 4 Components, migrations, factory and CRUD in one command based on database tables. Built for Laravel 13 + Livewire 4 + Tailwind CSS.
Requires
- php: ^8.2
- laravel/framework: ^11.0|^12.0
- livewire/livewire: ^3.0|^4.0
- power-components/livewire-powergrid: ^6.0
Requires (Dev)
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^11.0
This package is not auto-updated.
Last update: 2026-03-26 12:55:44 UTC
README
live4crud-tailwind
CRUD scaffolding para Laravel · Livewire 4 · PowerGrid 6 · Tailwind CSS
Genera un CRUD completo a partir de cualquier tabla de base de datos con un solo comando.
Modelo Eloquent · componente Livewire · tabla PowerGrid · vistas Blade · Factory · rutas.
Tabla de contenidos
- Requisitos
- Instalación
- Configuración inicial
- Generar un CRUD
- Archivos generados
- Detección automática
- Arquitectura y flujo de eventos
- Configuración
- Tailwind CSS
- PowerGrid — tabla de datos
- Personalizar stubs
- Ejemplo de salida completa
- Preguntas frecuentes
- Licencia
Requisitos
| Dependencia | Versión mínima |
|---|---|
| PHP | 8.2 |
| Laravel | 11 ó 12 |
| Livewire | 3 ó 4 |
| PowerGrid | 6.0 |
| Tailwind CSS | 3 ó 4 |
| Alpine.js | 3 (incluido con Livewire) |
Nota: PHP 8.1 no está soportado. El paquete usa
readonlyproperties y otros features de PHP 8.2.
Instalación
1 · Instalar el paquete vía Composer
composer require galax13a/live4crud-tailwind
El paquete se auto-registra gracias al package discovery de Laravel. No es necesario agregar nada al config/app.php.
2 · Verificar que Livewire y PowerGrid están instalados
Si no los tienes aún:
composer require livewire/livewire "^3.0|^4.0" composer require power-components/livewire-powergrid "^6.0"
3 · Ejecutar el instalador del paquete
php artisan live4crud:install
Esto hace automáticamente:
| Acción | Resultado |
|---|---|
| Publica config propia | config/live4crud.php |
| Publica config de PowerGrid | config/livewire-powergrid.php |
| Fuerza tema Tailwind en PowerGrid | 'theme' => 'tailwind' |
| Crea directorios necesarios | app/Livewire/, app/Livewire/Tables/, app/Models/, resources/views/livewire/ |
| Publica layout base | resources/views/layouts/app.blade.php |
| Marca en rutas | Agrega // live4crud-routes en routes/web.php |
4 · (Opcional) Publicar los stubs para personalizarlos
php artisan vendor:publish --tag=live4crud-stubs
# Genera: resources/stubs/live4crud/
Configuración inicial
Tailwind CSS
Si no tienes Tailwind instalado, sigue la guía oficial.
Luego agrega las rutas de las vistas de Livewire al array content en tailwind.config.js:
// tailwind.config.js module.exports = { content: [ './resources/views/**/*.blade.php', './resources/views/livewire/**/*.blade.php', './app/Livewire/**/*.php', ], theme: { extend: {}, }, plugins: [], }
PowerGrid — tema Tailwind
El instalador ya lo configura, pero puedes verificarlo en config/livewire-powergrid.php:
'theme' => \PowerComponents\LivewirePowerGrid\Themes\Tailwind::class,
Cargar los assets de Livewire en tu layout
Asegúrate de que tu layout tenga las directivas de Livewire:
{{-- resources/views/layouts/app.blade.php --}} <head> ... @livewireStyles </head> <body> ... @livewireScripts </body>
El layout publicado por
live4crud:installya incluye estas directivas.
Generar un CRUD
Importante: la tabla debe existir en la base de datos antes de ejecutar el comando. Crea y ejecuta tu migración primero.
php artisan live4crud:generate {tabla}
El comando te preguntará qué layout usar:
Layout:
[0] layouts.app
[1] layouts.admin
Ejemplos
# Tabla simple php artisan live4crud:generate products # Tabla con relación (tiene columna user_id) php artisan live4crud:generate orders # Tabla con FK a otra tabla php artisan live4crud:generate order_items # Tabla con enums php artisan live4crud:generate invoices
Archivos generados
Usando php artisan live4crud:generate products como ejemplo:
app/
├── Models/
│ └── Product.php ← Modelo Eloquent con relaciones auto-detectadas
└── Livewire/
├── Products.php ← Componente Livewire: modal + store/update/destroy
└── Tables/
└── ProductTable.php ← Tabla PowerGrid: búsqueda, filtros, paginación
database/
└── factories/
└── ProductFactory.php ← Factory con datos Faker según tipo de columna
resources/
└── views/
└── livewire/
└── products/
├── index.blade.php ← Página principal (@extends layout + @livewire)
├── view.blade.php ← Vista del componente: header + tabla + modal
└── modals.blade.php ← Modal crear/editar con campos Tailwind
routes/
└── web.php ← Ruta agregada: GET /products
Ruta generada
// routes/web.php (agregado automáticamente) Route::get('/products', fn() => view('livewire.products.index'))->name('products.index');
Accede en: http://tu-app.test/products
Detección automática
El generador inspecciona el esquema de la base de datos y adapta el código generado:
Tipos de columna → campo de formulario
| Tipo de columna | Campo generado |
|---|---|
varchar, string |
<input type="text"> |
int, bigint |
<input type="number"> |
decimal, float |
<input type="number"> |
text, longtext |
<textarea> |
date |
<input type="date"> |
datetime, timestamp |
<input type="date"> |
enum(...) |
<select> con todas las opciones del enum |
tinyint / columna active |
Toggle switch Tailwind |
columna enable / enabled |
Toggle switch verde Tailwind |
Relaciones detectadas automáticamente
| Condición | Resultado en el modelo |
|---|---|
| La tabla tiene FK hacia otra tabla | belongsTo() generado |
| Otra tabla tiene FK apuntando a esta | hasMany() o hasOne() |
Columna parent_id en la misma tabla |
parent() + children() (árbol) |
Columna user_id presente |
Scope de usuario (where user_id = auth()->id()) |
Tipos de filtro PowerGrid auto-generados
| Tipo de columna | Filtro PowerGrid |
|---|---|
varchar, string |
Filter::inputText() |
| FK a otra tabla | Filter::select() con datos del modelo relacionado |
enum |
Filter::select() con opciones del enum |
tinyint / booleano |
Filter::boolean() |
date, datetime |
Filter::datepicker() |
Arquitectura y flujo de eventos
El paquete genera dos componentes que se coordinan mediante eventos Livewire:
┌─────────────────────────────────────────┐
│ resources/views/livewire/ │
│ products/view.blade.php │
│ │
│ ┌──────────────────────────────────┐ │
│ │ App\Livewire\Tables\ProductTable│ │ ← PowerGrid
│ │ • Búsqueda global │ │ • Columnas ordenables
│ │ • Filtros por columna │ │ • Paginación
│ │ • [Editar] [Eliminar] │ │ • Toggle columnas
│ └──────┬──────────────┬────────────┘ │ • Responsive
│ │ │ │
│ dispatch dispatch │
│ live4crud-edit live4crud-delete │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ App\Livewire\Products │ │ ← Livewire CRUD
│ │ #[On('live4crud-edit')] │ │ • Abre modal
│ │ #[On('live4crud-delete')] │ │ • store() / update()
│ │ • Modal crear / editar │ │ • destroy()
│ └──────────────────────────────────┘ │ • Validación
│ │ │
│ dispatch pg:eventRefresh-ProductTable │
│ │ │
│ └──→ PowerGrid refresca sólo ───┘
└─────────────────────────────────────────┘
Paso a paso del flujo
- El usuario hace clic en "Editar" en la tabla PowerGrid
- PowerGrid ejecuta
Button::add('edit')->dispatch('live4crud-edit', ['id' => $row->id]) - El componente
Products.phprecibe el evento gracias a#[On('live4crud-edit')] - Se abre el modal con los datos del registro
- El usuario guarda →
update()se ejecuta - Al finalizar, se llama a
$this->dispatch('pg:eventRefresh-ProductTable') - PowerGrid refresca la tabla automáticamente sin recargar la página
Configuración
Publica el archivo de configuración:
php artisan vendor:publish --tag=live4crud-config
config/live4crud.php:
return [ /* | Ruta a los stubs personalizados. | 'default' usa los stubs del paquete. | Usa resource_path('stubs/live4crud') si los publicaste. */ 'stubs_path' => 'default', /* | Layout Blade que extenderán las vistas generadas. */ 'app_layout' => 'layouts.app', /* | Namespace para los modelos generados. */ 'model_namespace' => 'App\Models', /* | Namespace para los componentes Livewire generados. */ 'livewire_namespace' => 'App\Livewire', /* | Columnas que se excluyen de los formularios, fillable y vistas. */ 'unwanted_columns' => [ 'id', 'password', 'email_verified_at', 'remember_token', 'created_at', 'updated_at', 'deleted_at', ], ];
Tailwind CSS
Instalación rápida con Vite (recomendado para producción)
npm install -D tailwindcss postcss autoprefixer npx tailwindcss init -p
tailwind.config.js:
module.exports = { content: [ './resources/views/**/*.blade.php', './resources/views/livewire/**/*.blade.php', './app/Livewire/**/*.php', ], theme: { extend: {} }, plugins: [], }
resources/css/app.css:
@tailwind base; @tailwind components; @tailwind utilities;
vite.config.js:
import { defineConfig } from 'vite' import laravel from 'laravel-vite-plugin' export default defineConfig({ plugins: [ laravel({ input: ['resources/css/app.css', 'resources/js/app.js'], refresh: true }), ], })
En tu layout:
@vite(['resources/css/app.css', 'resources/js/app.js'])
CDN (sólo para desarrollo / pruebas)
El layout publicado por live4crud:install ya incluye el CDN de Tailwind para que funcione inmediatamente:
<script src="https://cdn.tailwindcss.com"></script>
Reemplaza el CDN por Vite antes de ir a producción.
PowerGrid — tabla de datos
La tabla generada en app/Livewire/Tables/{Model}Table.php extiende PowerGridComponent e incluye:
Búsqueda global
Habilitada con Header::make()->showSearchInput(). Busca en todas las columnas marcadas con ->searchable().
Filtros por columna
public function filters(): array { return [ Filter::inputText('name')->placeholder('Buscar por nombre'), Filter::select('category_id') ->dataSource(Category::all()) ->optionLabel('name') ->optionValue('id'), Filter::boolean('active'), Filter::datepicker('created_at'), ]; }
Botones de acción
Los botones despachan eventos Livewire que recibe el componente CRUD:
public function actions(Product $row): array { return [ Button::add('edit') ->slot('Editar') ->dispatch('live4crud-edit', ['id' => $row->id]), Button::add('delete') ->slot('Eliminar') ->dispatch('live4crud-delete', ['id' => $row->id]), ]; }
Añadir más columnas manualmente
Edita app/Livewire/Tables/ProductTable.php:
public function columns(): array { return [ Column::make('ID', 'id')->sortable()->hidden(), Column::make('Nombre', 'name')->sortable()->searchable(), Column::make('Precio', 'price')->sortable(), // Columna de relación (eager load en datasource) Column::make('Categoría', 'category_name')->sortable()->searchable(), Column::action('Acciones'), ]; } public function datasource(): Builder { return Product::query()->with('category') ->join('categories', 'categories.id', '=', 'products.category_id') ->select('products.*', 'categories.name as category_name'); }
Exportar a Excel / CSV
Instala el paquete de exportación de PowerGrid:
composer require power-components/livewire-powergrid-export
Y agrega en setUp():
use PowerComponents\LivewirePowerGrid\Exportable; public function setUp(): array { return [ Exportable::make('reporte-products') ->striped() ->type(Exportable::TYPE_XLS, Exportable::TYPE_CSV), Header::make()->showSearchInput()->showToggleColumns(), Footer::make()->showPerPage()->showRecordCount(), Responsive::make(), ]; }
Personalizar stubs
Puedes modificar completamente el código generado publicando los stubs:
php artisan vendor:publish --tag=live4crud-stubs
# → resources/stubs/live4crud/
Estructura de los stubs publicados:
resources/stubs/live4crud/
├── Model.stub ← Modelo Eloquent estándar
├── ModelUser.stub ← Modelo con scope de usuario
├── Livewire.stub ← Componente Livewire (modal + CRUD)
├── LivewireUser.stub ← Componente Livewire con filtro por user_id
├── PowerGridTable.stub ← Tabla PowerGrid estándar
├── PowerGridTableUser.stub ← Tabla PowerGrid filtrada por usuario
├── Factory.stub ← Factory Faker
└── views/
├── index.stub ← Página principal
├── view.stub ← Vista con tabla y header
├── modals.stub ← Modal crear/editar
├── form-field.stub ← Input texto/número
├── form-field-date.stub ← Input fecha
├── form-field-active.stub ← Toggle boolean (active)
├── form-field-enable.stub ← Toggle boolean (enable)
├── form-field-enum-table.stub ← Select con opciones enum
└── form-field-select-table.stub ← Select con datos de otra tabla (FK)
Luego indica al paquete que use tus stubs personalizados en config/live4crud.php:
'stubs_path' => resource_path('stubs/live4crud'),
Ejemplo de salida completa
Migración de ejemplo
Schema::create('products', function (Blueprint $table) { $table->id(); $table->foreignId('category_id')->constrained(); $table->string('name'); $table->text('description')->nullable(); $table->decimal('price', 10, 2); $table->enum('status', ['draft', 'active', 'archived'])->default('draft'); $table->boolean('active')->default(true); $table->date('available_at')->nullable(); $table->timestamps(); });
php artisan migrate php artisan live4crud:generate products
Modelo generado — app/Models/Product.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Product extends Model { use HasFactory; protected $table = 'products'; protected $fillable = [ 'category_id', 'name', 'description', 'price', 'status', 'active', 'available_at', ]; // Auto-detectado por FK public function category(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo(\App\Models\Category::class, 'category_id'); } }
Tabla PowerGrid — app/Livewire/Tables/ProductTable.php
<?php namespace App\Livewire\Tables; use App\Models\Product; use Illuminate\Database\Eloquent\Builder; use PowerComponents\LivewirePowerGrid\Button; use PowerComponents\LivewirePowerGrid\Column; use PowerComponents\LivewirePowerGrid\Facades\Filter; use PowerComponents\LivewirePowerGrid\Footer; use PowerComponents\LivewirePowerGrid\Header; use PowerComponents\LivewirePowerGrid\PowerGrid; use PowerComponents\LivewirePowerGrid\PowerGridComponent; use PowerComponents\LivewirePowerGrid\PowerGridFields; use PowerComponents\LivewirePowerGrid\Responsive; final class ProductTable extends PowerGridComponent { public string $tableName = 'ProductTable'; public function datasource(): Builder { return Product::query(); } public function fields(): PowerGridFields { return PowerGrid::fields() ->add('id') ->add('category_id') ->add('name') ->add('description') ->add('price') ->add('status') ->add('active', fn ($row) => $row->active ? '<span class="text-green-600 font-medium">✓</span>' : '<span class="text-red-400">✗</span>') ->add('available_at', fn ($row) => $row->available_at?->format('Y-m-d') ?? '—'); } public function columns(): array { return [ Column::make('ID', 'id')->sortable()->hidden(), Column::make('Category', 'category_id')->sortable()->searchable(), Column::make('Name', 'name')->sortable()->searchable(), Column::make('Description', 'description')->hidden(), Column::make('Price', 'price')->sortable()->searchable(), Column::make('Status', 'status')->sortable()->searchable(), Column::make('Active', 'active'), Column::make('Available At', 'available_at')->sortable()->searchable(), Column::action('Actions'), ]; } public function filters(): array { return [ Filter::inputText('name')->placeholder('Name'), Filter::inputText('price')->placeholder('Price'), Filter::select('status', 'status') ->dataSource(['draft' => 'draft', 'active' => 'active', 'archived' => 'archived']) ->optionLabel('label') ->optionValue('value'), Filter::select('category_id', 'category_id') ->dataSource(\App\Models\Category::all()) ->optionLabel('name') ->optionValue('id'), Filter::boolean('active'), Filter::datepicker('available_at'), ]; } public function actions(Product $row): array { return [ Button::add('edit') ->slot('Edit') ->class('inline-flex items-center rounded-md bg-amber-50 px-3 py-1.5 text-xs font-medium text-amber-700 ...') ->dispatch('live4crud-edit', ['id' => $row->id]), Button::add('delete') ->slot('Delete') ->class('inline-flex items-center rounded-md bg-red-50 px-3 py-1.5 text-xs font-medium text-red-700 ...') ->dispatch('live4crud-delete', ['id' => $row->id]), ]; } public function setUp(): array { return [ Header::make()->showSearchInput()->showToggleColumns(), Footer::make()->showPerPage(perPage: 15)->showRecordCount(), Responsive::make(), ]; } }
Componente CRUD — app/Livewire/Products.php
<?php namespace App\Livewire; use App\Models\Product; use Livewire\Attributes\On; use Livewire\Component; class Products extends Component { public ?int $selected_id = null; public bool $showModal = false; public bool $editMode = false; public $category_id; public $name; public $description; public $price; public $status; public $active; public $available_at; protected function rules(): array { return [ 'category_id' => 'required|integer', 'name' => 'required|string', 'description' => 'nullable|string', 'price' => 'required|numeric', 'status' => 'required|string', 'active' => 'nullable|integer', 'available_at' => 'nullable|date', ]; } public function render(): \Illuminate\View\View { return view('livewire.products.view'); } public function openCreate(): void { $this->_reset(); $this->showModal = true; } public function store(): void { $this->validate(); Product::create([...]); $this->showModal = false; session()->flash('success', 'Record created successfully.'); $this->dispatch('pg:eventRefresh-ProductTable'); // ← refresca PowerGrid } #[On('live4crud-edit')] public function edit(int $id): void { $record = Product::findOrFail($id); $this->selected_id = $record->id; $this->name = $record->name; // ... asigna todos los campos $this->editMode = true; $this->showModal = true; } public function update(): void { $this->validate(); Product::findOrFail($this->selected_id)->update([...]); $this->showModal = false; session()->flash('success', 'Record updated successfully.'); $this->dispatch('pg:eventRefresh-ProductTable'); } #[On('live4crud-delete')] public function destroy(int $id): void { Product::findOrFail($id)->delete(); session()->flash('success', 'Record deleted successfully.'); $this->dispatch('pg:eventRefresh-ProductTable'); } private function _reset(): void { ... } }
Preguntas frecuentes
¿Por qué el comando dice "Table not found"?
La tabla debe existir en la base de datos antes de generar el CRUD. Asegúrate de haber ejecutado php artisan migrate.
¿Puedo usar el paquete sin PowerGrid?
No directamente — la tabla de datos usa PowerGrid. Puedes publicar los stubs y modificar el PowerGridTable.stub para implementar tu propia solución de tabla.
¿Cómo agrego autenticación a las rutas generadas?
Edita routes/web.php y envuelve las rutas en un middleware:
Route::middleware(['auth'])->group(function () { Route::get('/products', fn() => view('livewire.products.index'))->name('products.index'); });
¿Funciona con soft deletes?
Agrega use SoftDeletes; al modelo generado y asegúrate de que tu migración tiene $table->softDeletes(). El generador excluye deleted_at de los formularios automáticamente.
¿Cómo cambio el número de filas por página?
En app/Livewire/Tables/ProductTable.php:
Footer::make()->showPerPage(perPage: 25)->showRecordCount(),
¿Puedo regenerar un CRUD existente?
Sí, el comando sobreescribe los archivos. Haz un backup o usa control de versiones antes de regenerar.
¿Soporta bases de datos SQLite?
La detección automática de relaciones y enums usa INFORMATION_SCHEMA, que no está disponible en SQLite. El comando funciona pero no detectará relaciones ni enums automáticamente.
Licencia
MIT — ver LICENSE.md.
Hecho con ♥ por galax13a