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.

Maintainers

Package info

github.com/galax13a/live4crud-tailwind

pkg:composer/galax13a/live4crud-tailwind

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-master 2026-03-25 04:49 UTC

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

Packagist Version PHP Version Laravel Livewire PowerGrid License: MIT

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

  1. Requisitos
  2. Instalación
  3. Configuración inicial
  4. Generar un CRUD
  5. Archivos generados
  6. Detección automática
  7. Arquitectura y flujo de eventos
  8. Configuración
  9. Tailwind CSS
  10. PowerGrid — tabla de datos
  11. Personalizar stubs
  12. Ejemplo de salida completa
  13. Preguntas frecuentes
  14. 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 readonly properties 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:install ya 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

  1. El usuario hace clic en "Editar" en la tabla PowerGrid
  2. PowerGrid ejecuta Button::add('edit')->dispatch('live4crud-edit', ['id' => $row->id])
  3. El componente Products.php recibe el evento gracias a #[On('live4crud-edit')]
  4. Se abre el modal con los datos del registro
  5. El usuario guarda → update() se ejecuta
  6. Al finalizar, se llama a $this->dispatch('pg:eventRefresh-ProductTable')
  7. 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