Automates the generation of full-stack CRUD modules from database schemas.
Install
mkdir -p .claude/skills/curd && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/5456" && unzip -o skill.zip -d .claude/skills/curd && rm skill.zipInstalls to .claude/skills/curd
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Generates complete CRUD module from database table definition. Orchestrates 9 sub-skills. Use when user says "create CRUD", "generate module from table", or provides a table structure.Key capabilities
- →Parse database table definitions
- →Generate backend migrations, models, and controllers
- →Create API routes and request validation
- →Scaffold frontend Vue pages
- →Implement import and export functionality
How it works
The skill orchestrates nine sub-tasks to scaffold a full-stack module based on a provided table structure and CatchAdmin conventions.
Inputs & outputs
When to use curd
- →Generate backend controllers from SQL
- →Create database migration files
- →Scaffold frontend CRUD views
About this skill
CatchAdmin CRUD Generator
一键生成完整 CRUD 模块 - 从数据表定义到前后端完整实现。
Sub-Skills (9 个子 Skills)
| Step | Skill | 生成文件 |
|---|---|---|
| 1 | 01-parse-table | - (解析输入) |
| 2 | 02-migration | database/migrations/*.php |
| 3 | 03-model | Models/{Model}.php |
| 4 | 04-controller | Http/Controllers/{Model}Controller.php |
| 5 | 05-request | Http/Requests/{Model}Request.php |
| 6 | 06-routes | routes/route.php |
| 7 | 07-export | Export/{Model}.php |
| 8 | 08-import | Import/{Model}.php |
| 9 | 09-vue-pages | index.vue + create.vue |
Input: Table Definition
Table: products
Fields:
- id (primary key)
- name (string, 100, required)
- category_id (foreign key -> categories)
- description (text, nullable)
- price (decimal 10,2, required)
- stock (integer, default 0)
- status (tinyint, default 1)
Output: Complete Module Structure
modules/{Module}/
├── Http/
│ ├── Controllers/{Model}Controller.php
│ └── Requests/{Model}Request.php
├── Models/{Model}.php
├── Export/{Model}.php
├── Import/{Model}.php
├── database/migrations/*.php
└── routes/route.php
web/src/views/{module}/
├── index.vue
└── create.vue
Pre-checks (执行前)
- 模块存在性:若
modules/{Module}不存在,先询问是否创建新模块。 - 文件冲突:目标文件已存在时,先询问覆盖或增量修改。
- 路由/权限前缀:检查
modules/{Module}/routes/route.php是否已有相同资源前缀。 - 依赖/配置:新增依赖或改动
config/前必须先询问。
Output Contract (输出契约)
- 必须生成 9 步完整产物(迁移/模型/控制器/请求/路由/导入导出/前端页)。
- 所有路径、类名、命名空间与
{Module}/{Model}/{table}一致。 - 前端
catch-table的api与后端路由前缀保持一致。
Consistency Validation (生成后校验)
- 迁移包含 CatchAdmin 标准字段:
creator_id/created_at/updated_at/deleted_at+ 索引。 - Model/Request 与迁移字段一致(类型/必填/默认值)。
- Route API 前缀与前端
api/permission前缀一致。
Fallback Rules (失败兜底)
- 输入缺失关键字段(如 table/fields)时必须先澄清,停止生成。
- 复杂关系或非标准业务逻辑需用户确认后再生成。
Workflow
Step 1: Parse Table Definition → 01-parse-table
Extract from user input:
- Table name (snake_case plural)
- Model name (PascalCase singular)
- Module name (PascalCase)
- Fields with types, constraints, and relationships
Step 2: Generate Migration → 02-migration
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('{table}', function (Blueprint $table) {
$table->id();
// Generated from field definitions
$table->string('name', 100);
$table->unsignedBigInteger('category_id')->default(0)->index();
$table->text('description')->nullable();
$table->decimal('price', 10, 2);
$table->unsignedInteger('stock')->default(0);
$table->string('image', 255)->nullable();
$table->tinyInteger('status')->default(1);
$table->unsignedInteger('sort')->default(0);
// CatchAdmin standard fields
$table->unsignedInteger('creator_id')->default(0);
$table->unsignedInteger('created_at')->default(0);
$table->unsignedInteger('updated_at')->default(0);
$table->unsignedInteger('deleted_at')->default(0);
$table->index(['status', 'deleted_at']);
});
}
public function down(): void
{
Schema::dropIfExists('{table}');
}
};
Step 3: Generate Model → 03-model
<?php
namespace Modules\{Module}\Models;
use Catch\Base\CatchModel as Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class {Model} extends Model
{
protected $table = '{table}';
protected $fillable = [
'id', 'name', 'category_id', 'description', 'price', 'stock',
'image', 'status', 'sort', 'creator_id', 'created_at', 'updated_at', 'deleted_at',
];
public array $searchable = [
'name' => 'like',
'category_id' => '=',
'status' => '=',
];
protected array $fields = [
'id', 'name', 'category_id', 'price', 'stock', 'status', 'created_at'
];
protected array $form = [
'name', 'category_id', 'description', 'price', 'stock', 'image', 'status', 'sort'
];
// Relationships (auto-detected from foreign keys)
public function category(): BelongsTo
{
return $this->belongsTo(Category::class, 'category_id');
}
}
Step 4: Generate Controller → 04-controller
<?php
namespace Modules\{Module}\Http\Controllers;
use Catch\Base\CatchController as Controller;
use Modules\{Module}\Http\Requests\{Model}Request;
use Modules\{Module}\Models\{Model};
use Modules\{Module}\Import\{Model} as {Model}Import;
use Illuminate\Http\Request;
class {Model}Controller extends Controller
{
public function __construct(
protected readonly {Model} $model
) {}
public function index()
{
return $this->model->getList();
}
public function store({Model}Request $request)
{
return $this->model->storeBy($request->all());
}
public function show($id): mixed
{
return $this->model->firstBy($id);
}
public function update($id, {Model}Request $request): mixed
{
return $this->model->updateBy($id, $request->all());
}
public function destroy($id)
{
return $this->model->deleteBy($id);
}
public function enable($id)
{
return $this->model->toggleBy($id);
}
public function export(): mixed
{
return {Model}::query()
->select('id', 'name', 'price', 'stock', 'created_at')
->get()
->download(['ID', 'Name', 'Price', 'Stock', 'Created At']);
}
public function import(Request $request, {Model}Import $import)
{
return $import->import($request->file('file'));
}
}
Step 5: Generate Request → 05-request
<?php
namespace Modules\{Module}\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class {Model}Request extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
// Auto-generated from field definitions
'name' => 'required|string|max:100',
'category_id' => 'required|integer|exists:categories,id',
'description' => 'nullable|string',
'price' => 'required|numeric|min:0',
'stock' => 'sometimes|integer|min:0',
'image' => 'nullable|string|max:255',
'status' => 'sometimes|integer|in:1,2',
'sort' => 'sometimes|integer|min:0',
];
}
}
Step 6: Generate Routes → 06-routes
<?php
use Illuminate\Support\Facades\Route;
use Modules\{Module}\Http\Controllers\{Model}Controller;
Route::apiResource('{resources}', {Model}Controller::class);
Route::put('{resources}/enable/{id}', [{Model}Controller::class, 'enable']);
Route::get('{resource}/export', [{Model}Controller::class, 'export']);
Route::post('{resource}/import', [{Model}Controller::class, 'import']);
Step 7: Generate Export → 07-export
<?php
namespace Modules\{Module}\Export;
use Catch\Support\Excel\Export;
class {Model} extends Export
{
protected array $header = ['ID', 'Name', 'Price', 'Stock', 'Created At'];
public function array(): array
{
return \Modules\{Module}\Models\{Model}::query()
->select('id', 'name', 'price', 'stock', 'created_at')
->get()
->toArray();
}
}
Step 8: Generate Import → 08-import
<?php
namespace Modules\{Module}\Import;
use Catch\Support\Excel\Import;
use Illuminate\Support\Collection;
use Modules\{Module}\Models\{Model};
class {Model}Import extends Import
{
public function collection(Collection $rows): void
{
$rows->skip(1)->each(function ($row) {
{Model}::create([
'name' => $row[0],
'price' => $row[1],
'stock' => $row[2],
]);
});
}
}
Step 9: Generate Vue Pages → 09-vue-pages
index.vue:
<template>
<div>
<catch-table
:columns="columns"
api="{module}/{resources}"
:exports="true"
:trash="true"
permission="{module}.{model}"
exportUrl="/{module}/export"
importUrl="/{module}/import"
:search-form="searchForm"
>
<template #dialog="row">
<Create :primary="row?.id" api="{module}/{resources}" />
</template>
</catch-table>
</div>
</template>
<script lang="ts" setup>
import Create from './create.vue'
const columns = [
{ type: 'selection' },
{ label: 'ID', prop: 'id', width: 80 },
{ label: 'Name', prop: 'name' },
{ label: 'Category', prop: 'category.name' },
{ label: 'Price', prop: 'price', width: 100 },
{ label: 'Stock', prop: 'stock', width: 80 },
{ label: 'Status', prop: 'status', switch: true, width: 80 },
{ label: 'Created At', prop: 'created_at', width: 180 },
{ type: 'operate', label: 'Actions', width: 150, update: true, destroy: true }
]
const searchForm = [
{ type: 'input', label: 'Name', name: 'name' },
{ type: 'select', label: 'Category', name: 'category_id', api: 'categories' },
{ type: 'select', label: 'Status', name: 'status', api: 'status' }
]
</script>
create.vue:
<template>
<el-form :model="formData" label-width="100px" ref="form" v-loading="loading">
<el-form-item label="Name" prop="name" :rules="[{ required: true, message: 'Required' }]">
<el-input v-model="formData.name" maxlength="100" />
</el-form-item>
<el-form-item label="Category" prop="category_id" :rules="[{ required: true, message: 'Required' }]">
<el-select v-model="formData.category_id" class="w-full">
<el-option v-for="item in categories" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
---
*Content truncated.*
When not to use it
- →When input table definitions are incomplete
- →When complex business logic requires manual confirmation
Limitations
- →Requires manual confirmation for custom business logic
- →Must resolve file conflicts if target files exist
How it compares
It automates the entire module lifecycle rather than manually creating individual backend and frontend files.
Compared to similar skills
curd side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| curd (this skill) | 1 | 6mo | No flags | Intermediate |
| enferno-dev | 1 | 7mo | Review | Beginner |
| laravel-specialist | 12 | 3mo | No flags | Intermediate |
| laravel-eloquent | 0 | 2mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by JaguarJack
View all by JaguarJack →You might also like
enferno-dev
level09
Development skill for Enferno Flask framework. Use when implementing features, fixing bugs, or writing code for Enferno-based applications. This includes creating models, API endpoints, Vue.js frontend components, database operations, or any development task within the Enferno ecosystem. Triggers: creating blueprints, adding models, building APIs, Vue/Vuetify components, Celery tasks, database migrations.
laravel-specialist
Jeffallan
Use when building Laravel 10+ applications requiring Eloquent ORM, API resources, or queue systems. Invoke for Laravel models, Livewire components, Sanctum authentication, Horizon queues.
laravel-eloquent
HoangNguyen0403
Write performant Eloquent queries with eager loading, reusable scopes, and strict lazy-loading prevention in Laravel. Use when defining model relationships, creating query scopes, or processing large datasets with chunk/cursor.
laravel-development
monicahq
Expert guidance for Laravel PHP development following best practices, SOLID principles, and Laravel conventions
sigma-audio-crm
YuvrajGaykhe
Use this skill for ALL development, debugging, review, refactoring, feature building, and integration work on the Sigma Audio Dealer & Sales Intelligence Platform. Trigger whenever the user mentions leads, dealers, follow-ups, RBAC, CRM, audit logs, Kanban pipeline, analytics, sales executive, Djang
add-feature
jujunjun110
新しい機能を追加する。ユーザーが機能の説明をしたとき、DDD4層に従って全レイヤーのファイルを生成する