devalade/crudify

Generate full CRUD with Livewire v4 components in Laravel

Maintainers

Package info

github.com/devalade/crudify

pkg:composer/devalade/crudify

Statistics

Installs: 11

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 12

1.5.2 2026-04-26 21:26 UTC

README

Tests

Generate full CRUD scaffolding for Laravel with a single command. Built for Livewire v4 and Laravel 11/12/13.

Crudify creates models, migrations, policies, Volt or Livewire CRUD pages, factories, and seeders — with support for relationships, soft deletes, file uploads, and searchable fields.

Installation

composer require devalade/crudify --dev

Quick Start

CLI

php artisan crudify:generate Post \
  --fields="title:string|body:text|is_published:boolean|published_at:datetime" \
  --relationships="user:belongsTo:User|category:belongsTo:Category|tags:belongsToMany:Tag" \
  --soft-delete

Volt single-file components are generated by default.

This generates SFCs directly in resources/views/pages/posts/:

  • index.blade.php — list with search, sort, pagination
  • create.blade.php — inline validation with #[Validate]
  • edit.blade.php — edit form with file upload support
  • show.blade.php — detail view

Routes are auto-discovered and registered by Crudify's service provider.

Classic Livewire

Generate classic Livewire page classes, Blade views, controllers, form requests, and routes:

php artisan crudify:generate Post \
  --fields="title:string|body:text|is_published:boolean" \
  --livewire

YAML

php artisan crudify:generate --file=post.yaml

post.yaml

model: Post

fields:
  title:
    type: string
    nullable: false
    unique: true

  slug:
    type: string
    nullable: false
    unique: true
    index: true

  body:
    type: text
    nullable: false

  is_published:
    type: boolean
    default: false

  published_at:
    type: datetime
    nullable: true

  # Single image upload
  featured_image:
    type: image
    nullable: true

  # Multiple file uploads
  gallery:
    type: image
    multiple: true
    nullable: true

  # Single file upload
  attachment:
    type: file
    nullable: true

relationships:
  user:
    type: belongsTo
    model: User

  category:
    type: belongsTo
    model: Category

  tags:
    type: belongsToMany
    model: Tag

  comments:
    type: hasMany
    model: Comment

searchable:
  - title
  - body

options:
  soft_deletes: true
  volt: true

Then run:

php artisan migrate

Visit /{resource} (e.g. /posts).

Field Syntax

CLI Format

name:type:modifier1:modifier2

Examples:

Field Definition Result
title:string string column
body:text text column
email:string:unique string with unique index
published_at:datetime:nullable nullable datetime
status:string:default:draft string with default value
user_id:foreign:users foreignId constrained to users table
views:integer:index integer with index
photo:image string column + file upload input
documents:file:multiple json column + multiple file upload input

Available Types

string, text, integer, bigint, float, double, decimal, boolean, date, datetime, timestamp, time, json, uuid, email, foreign, image, file

Modifiers

  • nullable — allows NULL values
  • unique — adds unique constraint
  • index — adds database index
  • default:value — sets default value
  • foreign:table — creates foreign key constraint
  • multiple — enables multiple file uploads (for image and file types)

File Uploads

Crudify supports single and multiple file/image uploads out of the box — for both Volt SFCs and classic Livewire pages.

Single Upload

php artisan crudify:generate Product \
  --fields="name:string|price:decimal|photo:image"

Generates:

  • string column for the file path
  • WithFileUploads trait in Volt components
  • File input with accept="image/*"
  • Automatic file storage to storage/app/public/
  • Old file deletion on edit

Multiple Uploads

php artisan crudify:generate Gallery \
  --fields="title:string|photos:image:multiple"

Generates:

  • json column for storing multiple paths
  • Array-cast in the model
  • Multiple file input (<input type="file" multiple>)
  • removePhotosFile() method for selective removal
  • Gallery preview grid on edit/show views

Relationships

Define Eloquent relationships in your model with a simple syntax.

CLI

php artisan crudify:generate Post \
  --fields="title:string|user_id:foreign:users" \
  --relationships="user:belongsTo:User|category:belongsTo:Category|tags:belongsToMany:Tag|comments:hasMany:Comment"

YAML

relationships:
  author:
    type: belongsTo
    model: User
    display: email
    label: Author
  comments:
    type: hasMany
    model: Comment
  profile:
    type: hasOne
    model: Profile
  tags:
    type: belongsToMany
    model: Tag
    display: slug
    label: Topics

Supported Types

  • belongsTo — generates dropdown in forms, eager-loaded in index
  • hasMany — generates model method only
  • hasOne — generates model method only
  • belongsToMany — generates checkbox group in forms, syncs on save

Relationships are automatically:

  • Added to the model with proper return types
  • Eager-loaded in generated index components
  • Validated in generated forms for classic Livewire mode
  • Displayed in index tables and show views
  • For belongsToMany, generates pivot migration and missing related model when needed

Relationship Display API

Use optional relationship metadata when default name field is not right.

CLI

php artisan crudify:generate Post \
  --fields="title:string" \
  --relationships="author:belongsTo:User:email|tags:belongsToMany:Tag:slug"

CLI format:

name:type:model[:display]

YAML

relationships:
  author:
    type: belongsTo
    model: User
    display: email
    label: Author

  tags:
    type: belongsToMany
    model: Tag
    display: slug
    label: Topics

Behavior:

  • display chooses field used in selects, checkbox labels, index badges, and show pages
  • label overrides section/table/form label shown to end users
  • belongsToMany appears in create/edit as checkboxes and in index/show as badges by default

belongsToMany Example

For a tags:belongsToMany:Tag relationship, the generator produces:

YAML:

model: Post

fields:
  title: string
  body: text

relationships:
  tags:
    type: belongsToMany
    model: Tag
    display: slug
    label: Topics

Run:

php artisan crudify:generate --file=post.yaml

Generated artifacts in default Volt mode:

  • app/Models/Post.php
  • app/Models/Tag.php if missing
  • database/migrations/*_create_posts_table.php
  • database/migrations/*_create_post_tag_table.php
  • Volt create/edit checkbox UI
  • index badges for related tags
  • selectedTagsIds state and sync logic

Generated Files

For a model named Post, Crudify generates by default:

File Description
app/Models/Post.php Eloquent model with $fillable, $casts, traits, and relationships
database/factories/PostFactory.php Factory with Faker methods mapped to field types
database/seeders/PostSeeder.php Seeder calling Post::factory()->count(10)->create()
database/migrations/xxxx_create_posts_table.php Migration with all columns and indexes
app/Policies/PostPolicy.php Authorization policy
resources/views/pages/posts/index.blade.php Volt index page with search, sort, pagination
resources/views/pages/posts/create.blade.php Volt create page with inline validation
resources/views/pages/posts/edit.blade.php Volt edit page
resources/views/pages/posts/show.blade.php Volt show page

Use --livewire to generate classic extras instead:

  • app/Http/Controllers/PostsController.php
  • app/Http/Requests/StorePostRequest.php
  • app/Http/Requests/UpdatePostRequest.php
  • app/Livewire/Pages/Posts/*.php
  • resources/views/livewire/pages/posts/*.blade.php
  • routes/web.php

Livewire 4 Compatible

All generated components use Livewire 4 syntax:

  • #[Validate] attributes on properties (fixes MissingRulesException)
  • #[Layout] and #[Title] attributes
  • WithFileUploads trait for file handling
  • wire:confirm for delete confirmations

UI Framework

Generated views use Flux UI components with Tailwind CSS. Generated Blade files include flux:* components plus Tailwind utility classes:

<flux:card>
  <flux:input wire:model.live="search" />
  <flux:table>...</flux:table>
</flux:card>

Install Flux in generated app:

composer require livewire/flux

@fluxAppearance
@vite(['resources/css/app.css', 'resources/js/app.js'])
@fluxScripts

Bootstrap setup files with:

php artisan crudify:setup

This command:

  • creates resources/css/app.css with Tailwind + Flux imports
  • creates resources/js/app.js
  • creates or patches common app layouts with @fluxAppearance, @vite(...), @livewireScripts, and @fluxScripts
  • patches Vite config to add @tailwindcss/vite when a Vite config file exists

Install and bootstrap everything automatically:

php artisan crudify:install
php artisan crudify:install --volt

crudify:install:

  • installs livewire/flux
  • installs livewire/volt when --volt used
  • installs tailwindcss and @tailwindcss/vite via npm when package.json exists
  • runs crudify:setup

Command Options

php artisan crudify:generate {model}
  --fields=          # Field definitions, separated by `|` or `;`
  --file=            # Path to YAML file (overrides --fields)
  --relationships=   # Relationships, separated by `|` or `;`
  --only=            # Generate only specific types (`model|migration` etc.)
  --skip=            # Skip specific types (`controller;policy` etc.)
  --soft-delete      # Add soft deletes
  --searchable=      # Comma-separated searchable fields
  --volt             # Explicitly generate Volt single-file components (default)
  --livewire         # Generate classic Livewire pages + controllers + requests + routes
  --force            # Overwrite existing files
  --dry-run          # Preview without writing files

Examples

Generate with file uploads:

php artisan crudify:generate Product \
  --fields="name:string|price:decimal|photo:image"

Generate with relationships:

php artisan crudify:generate Post \
  --fields="title:string|body:text" \
  --relationships="user:belongsTo:User|tags:belongsToMany:Tag"

Generate classic Livewire instead of Volt:

php artisan crudify:generate Post \
  --fields="title:string|body:text" \
  --livewire

Generate only model and migration:

php artisan crudify:generate Post --fields="title:string" --only=model|migration

Skip controllers (Livewire-only):

php artisan crudify:generate Post --fields="title:string" --skip=controller

Preview changes:

php artisan crudify:generate Post --fields="title:string" --dry-run

Customizing Stubs

Publish stubs to your application:

php artisan crudify:stubs

Then edit files in stubs/crudify/. The package will use your custom stubs on the next generation.

Requirements

  • PHP ^8.2
  • Laravel ^11.0, ^12.0 or ^13.0
  • Livewire ^4.0

Testing

composer test          # Run all tests
composer test:unit     # Run unit tests only
composer test:feature  # Run feature tests only
composer analyse       # Run static analysis
composer format        # Fix code style

License

MIT