CRUD Generator
Complete guide to using the module:make-crud command for rapid CRUD scaffolding in LaraDashboard modules. Automatically generates Models, Datatables, Livewire components, views, routes, and menu items.
CRUD Generator
The module:make-crud command provides rapid scaffolding for complete CRUD (Create, Read, Update, Delete) operations within LaraDashboard modules. It generates all necessary files including Models, Datatables, Livewire components, Blade views, routes, and menu items.
Prerequisites
Before using the CRUD generator, ensure you have:
- An existing module created with
php artisan module:make ModuleName - A database migration file defining your table structure (recommended)
- Understanding of module development
Quick Start
Option A: One Command (Recommended)
Generate everything including migration in a single command:
# 1. Create module (if not exists)
php artisan module:make Blog
# 2. Generate CRUD with fields (creates migration + all files)
php artisan module:make-crud Blog --model=Post --fields="title:string,author:string,content:text,is_published:boolean"
# 3. Run migration
php artisan migrate
# 4. Clear cache and visit /admin/blog/posts
php artisan optimize:clear
Option B: From Existing Migration
If you already have a migration:
# 1. Create module
php artisan module:make Blog
# 2. Create migration manually
php artisan module:make-migration create_blog_posts_table Blog
# Edit the migration file with your columns
# 3. Run migration
php artisan migrate
# 4. Generate CRUD (auto-detects columns from migration)
php artisan module:make-crud Blog --migration=create_blog_posts_table
# 5. Clear cache
php artisan optimize:clear
Option C: Interactive Mode
Let the command guide you:
# 1. Create module
php artisan module:make Blog
# 2. Run CRUD generator (will prompt for fields)
php artisan module:make-crud Blog --model=Post
# Answer "Yes" when asked to define fields
# Enter field names and types interactively
# 3. Run migration and clear cache
php artisan migrate && php artisan optimize:clear
Access Your CRUD
Visit your new CRUD pages:
- Index:
/admin/blog/posts - Create:
/admin/blog/posts/create - View:
/admin/blog/posts/{id} - Edit:
/admin/blog/posts/{id}/edit
Command Options
php artisan module:make-crud {module} [options]
Arguments
| Argument | Description |
|---|---|
module |
The name of the module (e.g., Blog, Sample) |
Options
| Option | Description |
|---|---|
--migration= |
The migration file name to parse columns from (e.g., create_posts_table) |
--model= |
The model name if not parsing from migration (e.g., Post, BlogPost) |
--fields= |
Field definitions to create migration automatically (e.g., "title:string,content:text") |
Examples
# Generate CRUD from migration (recommended for existing tables)
php artisan module:make-crud Sample --migration=create_sample_books_table
# Generate CRUD from model name (auto-detects migration)
php artisan module:make-crud Sample --model=Book
# Generate CRUD with fields - creates migration + all CRUD files in ONE command!
php artisan module:make-crud Blog --model=Post --fields="title:string,content:text,is_published:boolean"
# Interactive mode - prompts for field definitions
php artisan module:make-crud Blog --model=Article
# (Will ask if you want to define fields when no migration exists)
# Generate CRUD for a multi-word model
php artisan module:make-crud Blog --model=BlogPost --fields="title:string,excerpt:text,body:text"
Supported Field Types
When using --fields, you can use these types:
Basic Types
| Type | Database Column | Form Input | Example |
|---|---|---|---|
string |
VARCHAR(255) |
Text input | title:string |
text |
TEXT |
Textarea | content:text |
integer |
INT |
Number input | views:integer |
boolean |
TINYINT(1) |
Checkbox | is_active:boolean |
date |
DATE |
Date picker | published_date:date |
datetime |
DATETIME |
Datetime picker | expires_at:datetime |
decimal |
DECIMAL |
Number input | price:decimal |
json |
JSON |
Textarea | metadata:json |
Advanced UI Types
| Type | Database Column | Form Input | Example |
|---|---|---|---|
toggle |
TINYINT(1) |
Toggle switch | is_featured:toggle |
select |
VARCHAR(255) |
Dropdown | status:select:Active|Inactive|Pending |
editor |
TEXT |
Rich text editor (TinyMCE) | body:editor |
media |
FOREIGN KEY |
Media library selector | featured_image:media |
Note: The
mediatype integrates with the Media Library. It creates a foreign key to themediatable and uses the<x-media-selector>component for selecting images from the media library.
Select with Options
For select fields, you can define options inline using the pipe (|) separator:
# Format: field_name:select:Option1|Option2|Option3
--fields="status:select:Active|Inactive|Pending,priority:select:Low|Medium|High|Critical"
This generates a dropdown with the specified options.
Media Library Integration
The media type integrates with LaraDashboard's Media Library:
# Format: field_name:media
--fields="featured_image:media,gallery_image:media"
What it generates:
- Migration: Creates a foreign key column (
featured_image_id) referencing themediatable - Model: Adds a
belongsTorelationship and URL accessor - Form: Uses
<x-media-selector>component with image preview - Datatable: Shows image thumbnail with preview
- Show view: Displays the image with click-to-open
Generated Model Code:
use App\Models\Media;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
public function featuredImage(): BelongsTo
{
return $this->belongsTo(Media::class, 'featured_image_id');
}
public function getFeaturedImageUrlAttribute(): ?string
{
return $this->featured_image_id && $this->featuredImage
? asset('storage/media/' . $this->featuredImage->file_name)
: null;
}
Ready-to-Use Examples
Copy and paste these commands to quickly scaffold common CRUD modules. Each example creates everything you need in one command.
Blog / Posts
php artisan module:make-crud Blog --model=Post --fields="title:string,slug:string,excerpt:text,content:text,author:string,category:string,is_published:boolean,published_at:datetime" && php artisan migrate && php artisan optimize:clear
Visit: /admin/blog/posts
Products
php artisan module:make-crud Shop --model=Product --fields="name:string,sku:string,description:text,price:decimal,stock:integer,category:string,is_active:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/shop/products
Expenses / Finance
php artisan module:make-crud Finance --model=Expense --fields="title:string,amount:decimal,category:string,description:text,expense_date:date,is_paid:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/finance/expenses
Tasks / Todos
php artisan module:make-crud Project --model=Task --fields="title:string,description:text,priority:string,status:string,due_date:date,is_completed:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/project/tasks
Contacts / Leads
php artisan module:make-crud CRM --model=Contact --fields="name:string,email:string,phone:string,company:string,notes:text,source:string,is_customer:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/crm/contacts
Events / Calendar
php artisan module:make-crud Calendar --model=Event --fields="title:string,description:text,location:string,start_date:datetime,end_date:datetime,is_all_day:boolean,is_public:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/calendar/events
FAQs
php artisan module:make-crud Support --model=Faq --fields="question:string,answer:text,category:string,sort_order:integer,is_active:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/support/faqs
Testimonials
php artisan module:make-crud Marketing --model=Testimonial --fields="name:string,company:string,content:text,rating:integer,is_featured:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/marketing/testimonials
Team Members
php artisan module:make-crud Company --model=TeamMember --fields="name:string,role:string,bio:text,email:string,phone:string,sort_order:integer,is_active:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/company/team-members
Services
php artisan module:make-crud Business --model=Service --fields="name:string,description:text,price:decimal,duration:string,category:string,is_featured:boolean,is_active:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/business/services
Portfolio / Projects
php artisan module:make-crud Portfolio --model=Project --fields="title:string,description:text,client:string,category:string,url:string,completed_at:date,is_featured:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/portfolio/projects
Invoices
php artisan module:make-crud Billing --model=Invoice --fields="invoice_number:string,client_name:string,amount:decimal,tax:decimal,status:string,due_date:date,paid_at:datetime,notes:text" && php artisan migrate && php artisan optimize:clear
Visit: /admin/billing/invoices
Subscribers / Newsletter
php artisan module:make-crud Newsletter --model=Subscriber --fields="email:string,name:string,source:string,subscribed_at:datetime,is_active:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/newsletter/subscribers
Categories (Generic)
php artisan module:make-crud Catalog --model=Category --fields="name:string,slug:string,description:text,sort_order:integer,is_active:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/catalog/categories
Tags
php artisan module:make-crud Content --model=Tag --fields="name:string,slug:string,color:string,is_active:boolean" && php artisan migrate && php artisan optimize:clear
Visit: /admin/content/tags
Advanced Examples (Using New Field Types)
Blog with Rich Editor & Toggle
php artisan module:make-crud Blog --model=Article --fields="title:string,slug:string,excerpt:text,content:editor,category:select:Tech|Lifestyle|Business|News,is_featured:toggle,is_published:toggle,published_at:datetime" && php artisan migrate && php artisan optimize:clear
Visit: /admin/blog/articles
Products with Status Dropdown
php artisan module:make-crud Inventory --model=Product --fields="name:string,sku:string,description:editor,price:decimal,stock:integer,status:select:In Stock|Low Stock|Out of Stock|Discontinued,is_featured:toggle" && php artisan migrate && php artisan optimize:clear
Visit: /admin/inventory/products
Support Tickets with Priority & Status
php artisan module:make-crud Helpdesk --model=Ticket --fields="subject:string,description:editor,priority:select:Low|Medium|High|Critical,status:select:Open|In Progress|Resolved|Closed,is_urgent:toggle" && php artisan migrate && php artisan optimize:clear
Visit: /admin/helpdesk/tickets
Documents with File Upload
php artisan module:make-crud Documents --model=Document --fields="title:string,description:text,category:select:Contract|Invoice|Report|Other,attachment:file,is_public:toggle" && php artisan migrate && php artisan optimize:clear
Visit: /admin/documents/documents
Products with Media Library Image
php artisan module:make-crud Store --model=Product --fields="name:string,description:editor,featured_image:media,price:decimal,stock:integer,status:select:Draft|Published|Archived,is_featured:toggle" && php artisan migrate && php artisan optimize:clear
Visit: /admin/store/products
Comprehensive Example (All Field Types)
This example demonstrates all supported field types in a single command:
php artisan module:make-crud Demo --model=Item --fields="title:string,description:text,content:editor,featured_image:media,price:decimal,quantity:integer,status:select:Draft|Published|Archived,release_date:date,published_at:datetime,is_featured:toggle,metadata:json" && php artisan migrate && php artisan optimize:clear
Visit: /admin/demo/items
What this creates:
title- Text input (string)description- Textarea (text)content- Rich text editor with TinyMCE (editor)featured_image- Media library selector with image preview (media)price- Decimal number input (decimal)quantity- Integer number input (integer)status- Dropdown with Draft/Published/Archived options (select)release_date- Date picker (date)published_at- DateTime picker (datetime)is_featured- Toggle switch (toggle)metadata- JSON textarea (json)
Job Listings with Editor
php artisan module:make-crud Careers --model=Job --fields="title:string,location:string,type:select:Full-time|Part-time|Contract|Remote,description:editor,requirements:editor,salary_range:string,is_active:toggle" && php artisan migrate && php artisan optimize:clear
Visit: /admin/careers/jobs
Tip: After generating, customize the Datatable to add filters for
category,status, or other enum-like fields. See Adding Filters section below.
Generated Files
The command generates the following files:
Model
modules/{Module}/app/Models/{Model}.php
<?php
declare(strict_types=1);
namespace Modules\Blog\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $table = 'blog_posts';
protected $fillable = [
'title',
'author',
'content',
'category',
'is_published',
];
protected function casts(): array
{
return [
'is_published' => 'boolean',
];
}
}
Datatable Component
modules/{Module}/app/Livewire/Components/{Model}Datatable.php
Features:
- Sortable columns
- Searchable fields
- Pagination
- Action buttons (View, Edit, Delete)
- Livewire-based deletion with confirmation
Livewire Components
modules/{Module}/app/Livewire/Admin/{Models}/Index.php- List pagemodules/{Module}/app/Livewire/Admin/{Models}/Show.php- View pagemodules/{Module}/app/Livewire/Admin/{Models}/Create.php- Create formmodules/{Module}/app/Livewire/Admin/{Models}/Edit.php- Edit form with delete
Blade Views
modules/{Module}/resources/views/livewire/admin/{models}/index.blade.phpmodules/{Module}/resources/views/livewire/admin/{models}/show.blade.phpmodules/{Module}/resources/views/livewire/admin/{models}/create.blade.phpmodules/{Module}/resources/views/livewire/admin/{models}/edit.blade.php
Layout
modules/{Module}/resources/views/layouts/crud.blade.php
A clean layout extending the module's master layout, designed for CRUD pages with breadcrumb navigation.
Routes
Routes are automatically added to modules/{Module}/routes/web.php:
Route::get('posts', PostIndex::class)->name('posts.index');
Route::get('posts/create', PostCreate::class)->name('posts.create');
Route::get('posts/{post}', PostShow::class)->name('posts.show');
Route::get('posts/{post}/edit', PostEdit::class)->name('posts.edit');
Menu Item
A submenu item is automatically added to modules/{Module}/app/Services/MenuService.php:
// Posts submenu
$menu->setChildren(array_merge($menu->children, [
(new AdminMenuItem())->setAttributes([
'label' => __('Posts'),
'icon' => 'lucide:list',
'route' => route('admin.blog.posts.index'),
'active' => Route::is('admin.blog.posts.*'),
'id' => 'blog-posts',
'permissions' => [],
]),
]));
Column Type Mapping
The generator automatically maps database column types to appropriate form inputs:
| Database Type | Form Input | PHP Type | Blade Component |
|---|---|---|---|
string, char |
Text input | string |
<x-inputs.input> |
text, mediumText, longText |
Textarea | string |
<textarea> |
integer, bigInteger, tinyInteger |
Number input | int |
<x-inputs.input type="number"> |
boolean |
Checkbox | bool |
<input type="checkbox"> |
toggle |
Toggle switch | bool |
<x-inputs.toggle> |
select |
Dropdown | string |
<x-inputs.select> |
editor |
Rich text editor | string |
<x-text-editor> |
media |
Media library selector | ?int |
<x-media-selector> |
date |
Date input | date |
<x-inputs.input type="date"> |
datetime, timestamp |
Datetime input | datetime |
<x-inputs.input type="datetime-local"> |
foreignId |
Select (requires customization) | int |
<x-inputs.select> |
Customization Guide
Adding Permissions
Update the getActionCellPermissions() method in your Datatable:
public function getActionCellPermissions($item): array
{
return [
'view' => auth()->user()->can('post.view', $item),
'edit' => auth()->user()->can('post.edit', $item),
'delete' => auth()->user()->can('post.delete', $item),
];
}
Adding Filters
Add filter dropdowns to your Datatable for enum or category fields:
// Add property and queryString
public string $category = '';
public array $queryString = [
...parent::QUERY_STRING_DEFAULTS,
'category' => ['except' => ''],
];
public function updatingCategory(): void
{
$this->resetPage();
}
// Add getFilters method
public function getFilters(): array
{
$categories = Post::query()
->whereNotNull('category')
->where('category', '!=', '')
->distinct()
->pluck('category', 'category')
->toArray();
return [
[
'id' => 'category',
'label' => __('Category'),
'filterLabel' => __('Category'),
'icon' => 'lucide:folder',
'allLabel' => __('All Categories'),
'options' => $categories,
'selected' => $this->category,
],
];
}
// Update buildQuery to filter
protected function buildQuery(): QueryBuilder
{
return QueryBuilder::for(Post::query())
->when($this->search, function ($query) {
$query->where(function ($q) {
$q->where('title', 'like', "%{$this->search}%")
->orWhere('content', 'like', "%{$this->search}%");
});
})
->when($this->category, function ($query) {
$query->where('category', $this->category);
})
->orderBy($this->sort, $this->direction);
}
Customizing Form Fields
Edit the generated Blade views to customize form inputs:
{{-- Select dropdown for category --}}
<div>
<label for="category" class="form-label">{{ __('Category') }}</label>
<select wire:model="category" id="category" class="form-control">
<option value="">{{ __('Select Category') }}</option>
<option value="tech">{{ __('Technology') }}</option>
<option value="lifestyle">{{ __('Lifestyle') }}</option>
<option value="business">{{ __('Business') }}</option>
</select>
@error('category')
<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>
@enderror
</div>
{{-- Toggle for is_published --}}
<label class="flex items-center gap-3 cursor-pointer">
<input type="checkbox" wire:model="is_published" class="form-checkbox">
<div>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">
{{ __('Published') }}
</span>
<p class="text-xs text-gray-500 dark:text-gray-400">
{{ __('Make this post visible to the public') }}
</p>
</div>
</label>
Adding Relationships
For foreign key relationships, customize the form to include a select dropdown:
// In your Create/Edit component
public function mount(): void
{
$this->categories = Category::all();
// ... rest of mount
}
public function render(): View
{
return view('blog::livewire.admin.posts.create', [
'categories' => Category::all(),
]);
}
{{-- In your Blade view --}}
<x-inputs.select
wire:model="category_id"
name="category_id"
label="{{ __('Category') }}"
:options="$categories->pluck('name', 'id')"
:required="true"
/>
Custom Column Rendering
Add custom column rendering in your Datatable:
public function renderStatusColumn($item): string
{
$class = $item->is_published
? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
: 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300';
$label = $item->is_published ? __('Published') : __('Draft');
return "<span class='px-2 py-1 text-xs font-medium rounded-full {$class}'>{$label}</span>";
}
Best Practices
1. Always Create Migration First
The generator works best when it can parse your migration file to understand your table structure. This ensures:
- Correct fillable fields in the model
- Appropriate form inputs for each field type
- Proper validation rules
- Accurate datatable headers
2. Follow Naming Conventions
Use Laravel naming conventions for best results:
- Table:
module_models(e.g.,blog_posts,sample_books) - Model: Singular PascalCase (e.g.,
Post,Book) - Migration:
create_{table}_table
3. Run Tests After Generation
Always test your generated CRUD:
php artisan test --filter=Post
4. Customize Permissions
The generated code uses true for all permissions by default. Update these to use proper permission checks for production:
'view' => auth()->user()->can('post.view', $item),
5. Add Model Casts
Ensure your model has proper casts for boolean and date fields:
protected function casts(): array
{
return [
'is_published' => 'boolean',
'published_at' => 'datetime',
];
}
Troubleshooting
Routes Not Found
Clear the route cache after generating CRUD:
php artisan optimize:clear
Menu Not Showing
Ensure your MenuService.php has been updated. The generator looks for return $menu; to insert the submenu. If your MenuService has a different structure, add the menu item manually.
Boolean Field Type Error
If you get "Cannot assign int to property" errors for boolean fields, ensure:
- Your model has proper boolean casts
- The Edit component casts the value:
$this->is_published = (bool) $this->post->is_published;
Migration Not Detected
If the generator can't find your migration, specify the full migration name:
php artisan module:make-crud Blog --migration=2024_01_15_create_blog_posts_table
Example: Complete Blog Module
Here's a complete example of creating a Blog module with Posts:
# 1. Create module
php artisan module:make Blog
# 2. Create migration
php artisan module:make-migration create_blog_posts_table Blog
# Edit the migration file with your columns
# 3. Run migration
php artisan migrate
# 4. Generate CRUD
php artisan module:make-crud Blog --migration=create_blog_posts_table
# 5. Clear cache
php artisan optimize:clear
# 6. Visit /admin/blog/posts