study-api-v2/docs/CRUD代码生成模板.md

15 KiB
Raw Blame History

Laravel CRUD 代码生成模板(优化版)

架构说明

分层架构

  • BaseModel.php提供通用的权限控制tenant_id 自动过滤)、软删除、时间戳等基础功能
  • BaseService.php:提供标准的 CRUD 操作,统一事务处理和异常处理
  • BaseController.php提供统一的响应格式Success、SuccessPage、Field和参数获取方法
  • Controllers:只负责参数验证和调用服务层,无需关心权限控制和异常处理

权限控制机制

  • 查询时BaseModel 的全局作用域自动过滤当前用户的 tenant_id 数据
  • 创建时BaseModel 的 creating 钩子自动设置当前用户的 tenant_id
  • 更新/删除时BaseService 在操作前验证记录的 tenant_id 权限

目录结构规范

app/
├── Models/
│   └── [模块名]/
│       └── [表名Model].php
├── Services/
│   └── [模块名]/
│       └── [表名Service].php
├── Http/
│   ├── Controllers/
│   │   └── Admin/
│   │       └── [模块名]/
│   │           └── [表名Controller].php
│   └── Requests/
│       └── Admin/
│           └── [模块名]/
│               └── [表名Request].php
└── routes/
    └── admin/
        └── [模块名]_route.php

模型命名规范

  • 模型名必须与数据库表名保持一致的大驼峰格式
  • 例如:system_users 表 → SystemUsers 模型
  • 例如:system_role 表 → SystemRole 模型
  • 例如:user_profile 表 → UserProfile 模型
  • 不要使用简化名称,严格按照表名转换大驼峰

模板文件

1. 模型文件模板

文件路径app/Models/[模块名]/[表名].php

字段类型注释说明

  • int - 整数字段不可为null
  • int|null - 整数字段可为null
  • string - 字符串字段不可为null
  • string|null - 字符串字段可为null
  • float - 浮点数字段不可为null
  • float|null - 浮点数字段可为null
  • bool - 布尔字段不可为null
  • bool|null - 布尔字段可为null
  • \Carbon\Carbon - 日期时间字段不可为null
  • \Carbon\Carbon|null - 日期时间字段可为null
  • array - 数组字段通过JSON存储
  • array|null - 数组字段通过JSON存储可为null

注意根据数据库字段定义中的DEFAULT和NULL约束来确定是否需要添加|null

系统字段配置说明

BaseModel提供了两个配置选项来控制系统字段的自动维护

  1. $enableSystemFields - 控制是否自动维护系统字段

    • tenant_id - 租户ID
    • creator - 创建者
    • create_time - 创建时间
    • updater - 更新者
    • update_time - 更新时间
    • deleted - 删除标识
  2. $enableTenantScope - 控制是否启用租户隔离查询

使用建议

  • 主要业务表如School、User、Product等启用这两个配置
  • 关联表如StudentClass、UserRole等保持默认关闭
  • 日志表:只启用系统字段,不启用租户隔离
  • 配置表:根据实际情况决定
<?php

namespace App\Models\[模块名];

use App\Models\BaseModel;

/**
 * [中文名称]模型
 * @package App\Models\[模块名]
 * @property int $id 主键ID
 * @property string $name 名称
 * @property string $code 编码
 * @property int $status 状态
 * @property int $sort_order 排序
 * @property string $remark 备注
 * @property \Carbon\Carbon $created_at 创建时间
 * @property \Carbon\Carbon $updated_at 更新时间
 * @property \Carbon\Carbon|null $deleted_at 删除时间
 * @property int $tenant_id 租户ID
 */
class [表名] extends BaseModel
{
    protected $table = '[数据库表名]';

    /**
     * 启用系统字段自动维护
     * 包括tenant_id、creator、create_time、updater、update_time、deleted
     * 默认关闭主要业务表可以设置为true
     */
    protected $enableSystemFields = false;

    /**
     * 启用租户隔离
     * 默认关闭需要租户隔离的表可以设置为true
     */
    protected $enableTenantScope = false;

    protected $fillable = [
        '[字段1]',
        '[字段2]',
        // ... 其他可填充字段
    ];

    protected $casts = [
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    // 如果有特殊的查询作用域,可以添加
    // public function scopeActive($query)
    // {
    //     return $query->where('status', 1);
    // }

    // 如果有关联关系,可以添加
    // public function relatedModel()
    // {
    //     return $this->belongsTo(RelatedModel::class);
    // }
}

2. 服务文件模板

文件路径app/Services/[模块名]/[表名Service].php

<?php

namespace App\Services\[模块名];

use App\Models\[模块名]\[表名];
use App\Services\BaseService;
use Illuminate\Pagination\LengthAwarePaginator;

/**
 * [中文名称]服务类
 */
class [表名Service] extends BaseService
{
    protected string $modelClass = [表名]::class;

    /**
     * 获取[中文名称]列表
     */
    public function getList(array $params): LengthAwarePaginator
    {
        $query = [表名]::query();

        // 搜索条件
        if (!empty($params['keyword'])) {
            $query->where(function ($q) use ($params) {
                $q->where('name', 'like', '%' . $params['keyword'] . '%')
                  ->orWhere('code', 'like', '%' . $params['keyword'] . '%');
            });
        }

        // 状态筛选
        if (isset($params['status'])) {
            $query->where('status', $params['status']);
        }

        // 排序
        $query->orderBy('sort_order', 'asc')
              ->orderBy('id', 'desc');

        return $query->paginate($params['page_size'] ?? 15);
    }

    /**
     * 获取简单列表(用于下拉选择等)
     */
    public function getSimpleList(): array
    {
        return [表名]::select('id', 'name')
            ->where('status', 1)
            ->orderBy('sort_order', 'asc')
            ->get()
            ->toArray();
    }

    // 如果有特殊业务逻辑,可以重写父类方法
    // protected function beforeCreate(array &$data): void
    // {
    //     // 创建前的特殊处理
    // }

    // protected function afterCreate($model, array $data): void
    // {
    //     // 创建后的特殊处理
    // }
}

3. 控制器文件模板

文件路径app/Http/Controllers/Admin/[模块名]/[表名Controller].php

<?php

namespace App\Http\Controllers\Admin\[模块名];

use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\[模块名]\[表名Request];
use App\Services\[模块名]\[表名Service];
use Illuminate\Http\JsonResponse;

/**
 * [中文名称]控制器
 */
class [表名Controller] extends BaseController
{
    public function __construct(
        private [表名Service] $[表名变量]Service
    ) {}

    /**
     * 获取[中文名称]列表
     */
    public function list([表名Request] $request): JsonResponse
    {
        $params = $request->validated();
        $result = $this->[表名变量]Service->getList($params);

        return $this->SuccessPage($result->items(), $result->total());
    }

    /**
     * 获取简单列表
     */
    public function simpleList(): JsonResponse
    {
        $result = $this->[表名变量]Service->getSimpleList();
        return $this->Success($result);
    }

    /**
     * 获取[中文名称]详情
     */
    public function detail([表名Request] $request): JsonResponse
    {
        $params = $request->validated();
        $result = $this->[表名变量]Service->detail($params['id']);
        return $this->Success($result);
    }

    /**
     * 创建[中文名称]
     */
    public function create([表名Request] $request): JsonResponse
    {
        $data = $request->validated();
        $result = $this->[表名变量]Service->create($data);
        return $this->Success($result);
    }

    /**
     * 更新[中文名称]
     */
    public function update([表名Request] $request): JsonResponse
    {
        $params = $request->validated();
        $result = $this->[表名变量]Service->update($params['id'], $params);
        return $this->Success($result);
    }

    /**
     * 删除[中文名称]
     */
    public function delete([表名Request] $request): JsonResponse
    {
        $params = $request->validated();
        $this->[表名变量]Service->delete($params['id']);
        return $this->Success();
    }

    /**
     * 批量删除[中文名称]
     */
    public function batchDelete([表名Request] $request): JsonResponse
    {
        $params = $request->validated();
        $this->[表名变量]Service->batchDelete($params['ids']);
        return $this->Success();
    }
}

4. 验证文件模板

文件路径app/Http/Requests/Admin/[模块名]/[表名Request].php

<?php

namespace App\Http\Requests\Admin\[模块名];

use App\Http\Requests\BaseRequest;

/**
 * [中文名称]请求验证
 */
class [表名Request] extends BaseRequest
{
    /**
     * 获取验证规则
     */
    public function rules(): array
    {
        $action = $this->route()->getActionMethod();

        return match($action) {
            'list' => $this->listRules(),
            'detail' => $this->detailRules(),
            'create' => $this->createRules(),
            'update' => $this->updateRules(),
            'delete' => $this->deleteRules(),
            'batchDelete' => $this->batchDeleteRules(),
            'simpleList' => [],
            default => []
        };
    }

    /**
     * 列表查询验证规则
     */
    private function listRules(): array
    {
        return array_merge($this->getPaginationRules(), [
            'keyword' => ['sometimes', 'string', 'max:50'],
            'status' => ['sometimes', 'integer', 'in:0,1'],
        ]);
    }

    /**
     * 详情查询验证规则
     */
    private function detailRules(): array
    {
        return [
            'id' => ['required', 'integer', 'exists:[数据库表名],id'],
        ];
    }

    /**
     * 创建验证规则
     */
    private function createRules(): array
    {
        return [
            'name' => ['required', 'string', 'max:100'],
            'code' => ['required', 'string', 'max:50', 'unique:[数据库表名],code'],
            'status' => ['sometimes', 'integer', 'in:0,1'],
            'sort_order' => ['sometimes', 'integer', 'min:0'],
            'remark' => ['sometimes', 'string', 'max:200'],
        ];
    }

    /**
     * 更新验证规则
     */
    private function updateRules(): array
    {
        return [
            'id' => ['required', 'integer', 'exists:[数据库表名],id'],
            'name' => ['required', 'string', 'max:100'],
            'code' => ['required', 'string', 'max:50', 'unique:[数据库表名],code,' . $this->input('id')],
            'status' => ['sometimes', 'integer', 'in:0,1'],
            'sort_order' => ['sometimes', 'integer', 'min:0'],
            'remark' => ['sometimes', 'string', 'max:200'],
        ];
    }

    /**
     * 删除验证规则
     */
    private function deleteRules(): array
    {
        return [
            'id' => ['required', 'integer', 'exists:[数据库表名],id'],
        ];
    }

    /**
     * 批量删除验证规则
     */
    private function batchDeleteRules(): array
    {
        return [
            'ids' => ['required', 'array', 'min:1'],
            'ids.*' => ['integer', 'exists:[数据库表名],id'],
        ];
    }

    /**
     * 获取验证错误消息
     */
    public function messages(): array
    {
        return [
            'name.required' => '[中文名称]名称不能为空',
            'name.max' => '[中文名称]名称不能超过100个字符',
            'code.required' => '[中文名称]编码不能为空',
            'code.unique' => '[中文名称]编码已存在',
            'status.in' => '[中文名称]状态值无效',
            'ids.required' => '请选择要删除的[中文名称]',
            'ids.min' => '至少选择一条记录进行删除',
        ];
    }
}

5. 路由文件模板

文件路径routes/admin/[模块名]_route.php

<?php

use Illuminate\Support\Facades\Route;

/** -------------------------- [中文名称] ----------------------- */
Route::middleware("admin.auth")->group(function () {
    // 获取[中文名称]详情
    Route::match(['get', 'post'], "[路由前缀]/detail", [App\Http\Controllers\Admin\[模块名]\[表名Controller]::class, 'detail']);

    // 创建[中文名称]
    Route::match(['get', 'post'], "[路由前缀]/create", [App\Http\Controllers\Admin\[模块名]\[表名Controller]::class, 'create']);

    // 更新[中文名称]
    Route::match(['put', 'post'], "[路由前缀]/update", [App\Http\Controllers\Admin\[模块名]\[表名Controller]::class, 'update']);

    // 删除[中文名称]
    Route::match(['delete', 'post'], "[路由前缀]/delete", [App\Http\Controllers\Admin\[模块名]\[表名Controller]::class, 'delete']);

    // 获取[中文名称]列表
    Route::match(['get', 'post'], "[路由前缀]/list", [App\Http\Controllers\Admin\[模块名]\[表名Controller]::class, 'list']);

    // 获取简单列表
    Route::match(['get', 'post'], "[路由前缀]/simple/list", [App\Http\Controllers\Admin\[模块名]\[表名Controller]::class, 'simpleList']);

    // 批量删除[中文名称]
    Route::match(['delete', 'post'], "[路由前缀]/batch/delete", [App\Http\Controllers\Admin\[模块名]\[表名Controller]::class, 'batchDelete']);
});

使用示例

假设我们要为school_class表生成 CRUD 代码:

替换变量说明

  • [模块名]Schools
  • [表名]SchoolClass
  • [表名变量]schoolClass
  • [数据库表名]school_class
  • [中文名称]班级
  • [路由前缀]school/class

生成的文件结构

app/
├── Models/Schools/SchoolClass.php
├── Services/Schools/SchoolClassService.php
├── Http/
│   ├── Controllers/Admin/Schools/SchoolClassController.php
│   └── Requests/Admin/Schools/SchoolClassRequest.php
└── routes/admin/schools_route.php

控制器方法命名规范

  • list() - 获取列表(替代 index
  • detail() - 获取详情(替代 show
  • create() - 创建(替代 store
  • update() - 更新
  • delete() - 删除(替代 destroy
  • batchDelete() - 批量删除
  • simpleList() - 获取简单列表

验证规则命名规范

验证方法根据控制器方法名自动匹配:

  • listRules() - 列表查询验证
  • detailRules() - 详情查询验证
  • createRules() - 创建验证
  • updateRules() - 更新验证
  • deleteRules() - 删除验证
  • batchDeleteRules() - 批量删除验证

核心优势

  1. 代码量减少 60%以上:通过 BaseModel、BaseService 提供通用功能
  2. 权限控制自动化tenant_id 完全由框架层自动处理
  3. 异常处理统一化:控制器无需 try-catch全局异常处理器统一处理
  4. 命名更加直观:使用功能性命名而非传统 RESTful 命名
  5. 开发效率极高:新模块只需实现特殊业务逻辑
  6. 维护成本降低:统一的代码结构和规范