免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Laravel 生产级架构模式完整指南:分层目录、Eloquent ORM、队列事件与 API 设计(ECC laravel-patterns)

Laravel 生产级架构模式完整指南:分层目录、Eloquent ORM、队列事件与 API 设计(ECC laravel-patterns) Laravel 生产级架构模式完整指南分层目录、Eloquent ORM、队列事件与 API 设计ECC laravel-patterns【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC本篇技术指南系统讲解 ECCThe agent harness performance optimization system仓库中laravel-patterns技能文档所沉淀的 Laravel 生产级架构实践覆盖从目录分层、控制器/服务/Action 职责划分到路由模型绑定、Eloquent 模型模式、迁移、Form Request 校验、API Resources以及队列事件与缓存配置的完整落地方案。读者读完将掌握一套可直接应用于 Laravel Web 应用与 API 项目的分层架构规范并了解该技能在 ECC 项目中与 PHP 审查代理、技术栈识别及项目模板是如何联动生效的。laravel-patterns是 ECC 为「构建或评审 Laravel 应用」提供的一等技能资产其源文件保存在 skills/laravel-patterns/SKILL.md并同步维护了西班牙语docs/es/skills/laravel-patterns/SKILL.md等多语言版本。围绕同一主题仓库还配齐了 laravel-security、laravel-tdd、laravel-verification 与 laravel-plugin-discovery 等技能构成完整的 Laravel 工程化闭环。技能定位何时使用 laravel-patterns根据技能文档的description与When to Use一节laravel-patterns面向四类核心场景构建 Laravel Web 应用或 API从零搭建或重构时以此为架构基线组织控制器、服务与领域逻辑回答「业务逻辑该放哪一层」这一高频问题使用 Eloquent 模型与关系规范模型配置、cast、scope 与关联预加载设计基于 Resources 与分页的 API统一响应结构与序列化形态引入队列、事件、缓存与后台 Job为耗时的副作用逻辑选择正确的异步载体。在 ECC 的整套工程体系中这一技能被显式挂载到 PHP/Laravel 项目栈之上。见 config/project-stack-mappings.json 中的php-laravel映射条目当项目目录出现composer.json、artisan、composer.lock等指示文件时即判定为 PHP/Laravel 栈并同时装载laravel-patterns、laravel-tdd、laravel-verification、laravel-security、tdd-workflow等技能与对应命令。换言之当 ECC 在某个 Laravel 仓库中运行时本节介绍的架构原则会自动成为编码与评审的默认参照。底层框架识别逻辑见 scripts/lib/project-detect.jsframework: laravel, language: php, markers: [artisan], packageKeys: [laravel/framework]。项目结构与分层边界技能文档强调使用「带清晰层边界的常规 Laravel 布局」HTTP、services/actions、models 三层。推荐布局如下app/ ├── Actions/ # 单一用途用例 ├── Console/ ├── Events/ ├── Exceptions/ ├── Http/ │ ├── Controllers/ │ ├── Middleware/ │ ├── Requests/ # Form Request 校验 │ └── Resources/ # API resources ├── Jobs/ ├── Models/ ├── Policies/ ├── Providers/ ├── Services/ # 协调性领域服务 └── Support/ config/ database/ ├── factories/ ├── migrations/ └── seeders/ resources/ ├── views/ └── lang/ routes/ ├── api.php ├── web.php └── console.php这套结构与 ECC 附带的真实 Laravel 项目模板 examples/laravel-api-CLAUDE.md 中的 File Structure 完全一致印证其可直接落地。其背后的核心原则是Controller 只做传输层的事鉴权、校验、序列化、状态码不承载业务规则Service 承担协调职责编排多个 Action 或多个仓储完成一个完整业务流程如「下单」Action 封装单一目的用例一个 Action 只回答一个问题便于独立测试与复用。在 rules/php/patterns.md 中这一原则被提炼为“Thin Controllers, Explicit Services”控制器聚焦 transport业务规则下沉到无需 HTTP 启动即可测试的应用/领域服务中。Controller → Service → Action 的调用链实现技能文档给出的示例将「创建订单」拆为 Action 与 Controller 两层final class CreateOrderAction { public function __construct(private OrderRepository $orders) {} public function handle(CreateOrderData $data): Order { return $this-orders-create($data); } } final class OrdersController extends Controller { public function __construct(private CreateOrderAction $createOrder) {} public function store(StoreOrderRequest $request): JsonResponse { $order $this-createOrder-handle($request-toDto()); return response()-json([ success true, data OrderResource::make($order), error null, meta null, ], 201); } }值得注意的写法细节构造器注入Controller 与 Action 均通过构造函数注入依赖OrderRepository、CreateOrderAction完全交给 Laravel 服务容器自动解析避免app()服务定位器式调用这与 rules/php/patterns.md 中「依赖接口/窄契约而非框架全局、通过构造器传递协作者」的 DI 约定一一对应。HTTP 入参先转 DTOController 不直接消费$request-all()而是由 Form Request 的toDto()产出CreateOrderData数据传输对象。返回统一信封success / data / error / meta四字段响应结构贯穿所有 API 响应。配套项目模板 examples/laravel-api-CLAUDE.md 给出了该信封的 JSON 形态{ success: true, data: {...: ...}, error: null, meta: {page: 1, per_page: 25, total: 120} }若业务流程更复杂可在 Action 之上再叠加一层协调性 Service。模板 examples/laravel-api-CLAUDE.md 展示了完整形态OrderService构造注入CreateOrderActionplaceOrder()仅转发调用Controller 注入OrderService形成Controller → Service → Action的三段链路。路由、隐式绑定与作用域绑定资源路由与中间件优先使用路由模型绑定与资源控制器提升可读性use Illuminate\Support\Facades\Route; Route::middleware(auth:sanctum)-group(function () { Route::apiResource(projects, ProjectController::class); });apiResource会按 REST 惯例一次性注册 index/store/show/update/destroy 五条路由auth:sanctum中间件为整个组启用 API Token 鉴权ECC 的 Laravel 模板默认以 Sanctum 作为 API 认证方案见 examples/laravel-api-CLAUDE.md。作用域绑定Scoped Binding防止跨租户访问当路由中出现父子两级模型参数时使用scopeBindings()让 Laravel 自动把父模型的关联约束应用到子模型解析上从而防止跨账户/跨租户访问Route::scopeBindings()-group(function () { Route::get(/accounts/{account}/projects/{project}, [ProjectController::class, show]); });上例中{project}的解析会限定为「属于该{account}的 project」任一参数不匹配都会直接返回 404 而非 403从路由层就堵死越权通道。嵌套路由与绑定命名的一致性技能文档特别警告了两类易错点避免双重嵌套与前后缀不一致例如conversation与conversations混用造成 URL 语义混乱参数名必须与绑定的模型类名单数形式一致{conversation}对应Conversation模型。嵌套路由的推荐写法use App\Http\Controllers\Api\ConversationController; use App\Http\Controllers\Api\MessageController; use Illuminate\Support\Facades\Route; Route::middleware(auth:sanctum)-prefix(conversations)-group(function () { Route::post(/, [ConversationController::class, store])-name(conversations.store); Route::scopeBindings()-group(function () { Route::get(/{conversation}, [ConversationController::class, show]) -name(conversations.show); Route::post(/{conversation}/messages, [MessageController::class, store]) -name(conversation-messages.store); Route::get(/{conversation}/messages/{message}, [MessageController::class, show]) -name(conversation-messages.show); }); });这里Route::prefix(conversations)-group()统一了集合级前缀内层scopeBindings()保证{message}必须属于{conversation}命名时统一conversations.*风格跨控制器引用消息则用语义明确的conversation-messages.*。显式绑定与自定义绑定解析当 URL 参数需要解析为与参数名不同的模型类时例如将{conversation}绑定到AiConversation定义显式模型绑定use App\Models\AiConversation; use Illuminate\Support\Facades\Route; Route::model(conversation, AiConversation::class);若需要更复杂的绑定逻辑如按 slug 而非主键查询、或叠加额外过滤技能文档给出两条路使用Route::bind()注册自定义解析回调或在模型类上实现resolveRouteBinding()方法。服务容器绑定接口到实现的依赖装配为保证依赖注入清晰、可替换技能文档推荐在 Service Provider 的register()中把「接口/抽象」绑定到「具体实现」use App\Repositories\EloquentOrderRepository; use App\Repositories\OrderRepository; use Illuminate\Support\ServiceProvider; final class AppServiceProvider extends ServiceProvider { public function register(): void { $this-app-bind(OrderRepository::class, EloquentOrderRepository::class); } }bind()表示每次解析都会构造新实例若希望整个应用生命周期内复用同一实例可将bind换成singleton。这一模式配合「依赖接口而非具体类」的约定见 rules/php/patterns.md使 Action/Service 可面向仓储契约编程——在测试中用内存假实现替换 Eloquent 实现即可完成单元测试无需数据库。Eloquent 模型模式模型基础配置技能文档给出的模型规范模板同时覆盖了 fillable、cast 与关系final class Project extends Model { use HasFactory; protected $fillable [name, owner_id, status]; protected $casts [ status ProjectStatus::class, archived_at datetime, ]; public function owner(): BelongsTo { return $this-belongsTo(User::class, owner_id); } public function scopeActive(Builder $query): Builder { return $query-whereNull(archived_at); } }几点实践要点$fillable白名单是安全底线ECC 的 PHP 审查代理把「$guarded []或直接create($request-all())造成的大规模赋值Mass Assignment」列为 CRITICAL 级安全问题规范做法是显式列出可批量赋值的字段见 agents/php-reviewer.md。$casts保证类型一致将status映射到 PHP 枚举类ProjectStatus将时间戳字段声明为datetime让领域取值在应用层始终具备正确类型。类声明为final除非设计为被继承否则服务与模型一律final这是 ECC PHP 规范PSR-12 严格类型的一部分。自定义 Cast 与值对象对枚举或带约束的值金额、标识符、日期区间技能文档要求使用自定义 cast 或值对象实现严格类型use Illuminate\Database\Eloquent\Casts\Attribute; protected $casts [ status ProjectStatus::class, ];protected function budgetCents(): Attribute { return Attribute::make( get: fn (int $value) Money::fromCents($value), set: fn (Money $money) $money-toCents(), ); }第二个示例演示了 Laravel 9 的Attribute::make访问器写法数据库存整数「分」应用层读出的是Money值对象写入时自动转回整数货币计算永远不会出现浮点误差。这也呼应 rules/php/patterns.md 中「用 DTO/值对象替代形状复杂的关联数组」的 PHP 层约定。Eager Loading 规避 N1N1 查询是 ECC PHP 审查代理明确列为 HIGH 级的问题见 agents/php-reviewer.mdmissingwith()for relationships in loops or serialization。技能文档给出的标准解$orders Order::query() -with([customer, items.product]) -latest() -paginate(25);用一条查询预加载嵌套两层关系customer、items.product避免循环内逐条查询latest()按时间倒序paginate(25)直接产出分页器无需手写 LIMIT/OFFSET。Query Objects复杂过滤的封装当单个模型的过滤条件组合复杂多筛选、多排序、需复用技能文档建议抽出 Query Object 类封装 builderfinal class ProjectQuery { public function __construct(private Builder $query) {} public function ownedBy(int $userId): self { $query clone $this-query; return new self($query-where(owner_id, $userId)); } public function active(): self { $query clone $this-query; return new self($query-whereNull(archived_at)); } public function builder(): Builder { return $this-query; } }设计要点在于每个过滤方法都克隆当前 query再返回新的 Query Object 实例保持不可变性、支持链式组合(new ProjectQuery(Project::query()))-ownedBy($id)-active()-builder()同时避免对传入 Builder 的意外原地修改。它比把过滤堆进 Service 更内聚也比全局可变状态更安全。Global Scopes 与 Soft Deletes默认过滤如「只看未归档」可用全局作用域固化需要可恢复删除的记录则启用SoftDeletesuse Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Builder; final class Project extends Model { use SoftDeletes; protected static function booted(): void { static::addGlobalScope(active, function (Builder $builder): void { $builder-whereNull(archived_at); }); } }技能文档特别给出了一条反模式警告同一过滤条件不要同时用全局作用域和命名作用域实现——两者叠加会导致过滤行为重复或难以追踪除非刻意追求分层行为否则二选一。SoftDeletes则让delete()变为写入deleted_at的逻辑删除withTrashed()可恢复查询。命名 Query Scope可复用过滤器轻量、单处的过滤优先用命名作用域use Illuminate\Database\Eloquent\Builder; final class Project extends Model { public function scopeOwnedBy(Builder $query, int $userId): Builder { return $query-where(owner_id, $userId); } } // En servicio, repositorio, etc. $projects Project::ownedBy($user-id)-get();命名作用域通过scopeXxx方法定义、以xxx()静态调用可继续接-where()-get()链式扩展。上例注释表明其典型调用位置是 Service、Repository 等业务层。事务包裹多步更新涉及多张表或多次写操作必须放进数据库事务use Illuminate\Support\Facades\DB; DB::transaction(function (): void { $order-update([status paid]); $order-items()-update([paid_at now()]); });闭包内任一语句抛出异常都会触发整体回滚避免「主订单已标记已支付、明细却未更新」这类中间态。DB::transaction支持嵌套基于保存点也支持第二个参数传入隔离级别与重试次数多步业务更新应默认以此收口。迁移命名约定与匿名类命名约定迁移文件名带时间戳前缀YYYY_MM_DD_HHMMSS_create_users_table.php保证执行顺序确定迁移使用匿名类return new class extends Migration不写命名类文件名即意图声明表名默认snake_case复数orders、users。迁移示例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(orders, function (Blueprint $table): void { $table-id(); $table-foreignId(customer_id)-constrained()-cascadeOnDelete(); $table-string(status, 32)-index(); $table-unsignedInteger(total_cents); $table-timestamps(); }); } public function down(): void { Schema::dropIfExists(orders); } };本示例同时示范了几条数据库最佳实践外键用foreignId()-constrained()声明式关联并以cascadeOnDelete()级联清理status加索引以加速按状态的 where/orderBy 查询ECC 审查代理明确要求「任何出现在 where 或 orderBy 中的列都建索引」见 examples/laravel-api-CLAUDE.md金额使用无符号整数分total_cents而非浮点。迁移文件应提交进版本控制。Form Requests 与校验校验留在请求层输入转成 DTO技能文档的核心主张是校验逻辑放进 Form Request而不是控制器方法体控制器不再接触$request-all()。use App\Models\Order; final class StoreOrderRequest extends FormRequest { public function authorize(): bool { return $this-user()?-can(create, Order::class) ?? false; } public function rules(): array { return [ customer_id [required, integer, exists:customers,id], items [required, array, min:1], items.*.sku [required, string], items.*.quantity [required, integer, min:1], ]; } public function toDto(): CreateOrderData { return new CreateOrderData( customerId: (int) $this-validated(customer_id), items: $this-validated(items), ); } }authorize()做授权通过$this-user()?-can(create, Order::class)委托 Policy 完成模型级授权未登录返回 false。rules()只声明校验数组语法支持exists:customers,id引用完整性校验与items.*.sku嵌套数组逐项校验校验失败由框架自动转为 422 响应。toDto()产出 DTO只取$this-validated()的字段显式转型后构造CreateOrderData。业务层不感知 HTTP 请求对象派生字段也绝不信任原始 payload见 examples/laravel-api-CLAUDE.md。API Resources 与统一分页响应技能文档要求 API 响应保持「Resources 分页」的一致形态$projects Project::query()-active()-paginate(25); return response()-json([ success true, data ProjectResource::collection($projects-items()), error null, meta [ page $projects-currentPage(), per_page $projects-perPage(), total $projects-total(), ], ]);ProjectResource::collection(...)负责逐项序列化隐藏敏感字段、格式化日期、扁平化关联meta块携带分页信息便于客户端实现「加载更多」。Resource 本身继承JsonResource并实现toArray(Request $request): array模板示例见 examples/laravel-api-CLAUDE.md。注意此处data传的是$projects-items()当前页集合分页状态单列在meta从而在保持分页信息的同时让data字段始终是纯资源数组便于前端直接消费。事件、Job 与队列异步副作用的标准载具技能文档为异步处理给出了三条核心主张用领域事件承载副作用邮件通知、埋点统计等不应阻塞主流程而应在业务完成后event(new OrderCreated($order))派发由监听器处理用队列 Job 承载慢任务报表生成、数据导出、Webhook 推送等耗时工作放入队列异步执行Handler 尽量幂等设计可重复执行不产生重复副作用的 handler并配置重试与退避retries/backoff。配套模板中的队列 Job 展示了标准骨架examples/laravel-api-CLAUDE.mduse Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use App\Repositories\OrderRepository; use App\Services\OrderMailer; final class SendOrderConfirmation implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function __construct(private int $orderId) {} public function handle(OrderRepository $orders, OrderMailer $mailer): void { $order $orders-findOrFail($this-orderId); $mailer-sendOrderConfirmation($order); } }要点实现ShouldQueue使 Job 默认异步构造器只保存orderId这类标量而非整个模型配合SerializesModels避免把完整模型快照序列化进队列handle()中依赖通过容器注入——即便延迟执行解析出的仓储与邮件服务仍是最新绑定这正是「面向接口编程 容器注入」在异步边界的延伸。ECC 的 PHP 审查代理也将「队列任务幂等性」列为 Laravel 专项检查项见 agents/php-reviewer.md。缓存昂贵读取的加速与失效策略技能文档给出三条缓存纪律缓存高频读取的端点与昂贵查询例如热点列表、聚合统计、外部 API 结果用Cache::remember(key, $ttl, fn () ...)包裹在模型事件上失效缓存在模型的created / updated / deleted事件回调中清除对应缓存键避免读到脏数据关联数据用缓存标签便于整体失效Cache::tags([orders, user:.$id])-put(...)失效时按标签一键清理要求驱动支持 tags如 Redis 或 array 驱动。配置与环境secret 与 config 分离密钥只放.env数据库口令、APP_KEY、第三方凭据配置逻辑放config/*.php代码中一律通过config(key)读取环境差异化覆盖通过.env按环境注入不同值config/*.php提供默认值与env()回退生产环境执行php artisan config:cache将全部配置编译为单一缓存文件既减少每次请求解析.env的开销也能在部署后快速发现残缺配置注意使用config:cache后env()仅在配置文件中可调用业务代码应改用config()。技能在 ECC 中的配套落地laravel-patterns并非孤立文档它处于 ECC 完整的 Laravel 工程体系正中技能映射config/project-stack-mappings.json 将 PHP/Laravel 栈composer.json/artisan/composer.lock指示文件关联到laravel-patterns及配套技能并为该栈预设build: composer install、test: php artisan test / phpunit / pest、lint: phpstan / pint等标准命令技术栈识别scripts/lib/project-detect.js 以artisan文件与composer.json中的laravel/framework依赖作为 Laravel 判定信号规则衔接rules/php/patterns.md 将 PHP 通用约定薄控制器、DTO/值对象、构造器注入、隔离 ORM 与领域决策与本文的 Laravel 具体实现打通并显式指向本技能审查代理agents/php-reviewer.md 把 N1、$fillable/$casts缺失、控制器夹带业务逻辑、绕过 FormRequest、whereRaw拼接用户输入等本文反面场景列为评审红线静态检查推荐phpstan analyse --level max、psalm、pint --test真实模板examples/laravel-api-CLAUDE.md 给出了 PHP 8.2 / Laravel 11.x / PostgreSQL / Redis / Horizon / Pest 的整仓规范将上述模式连同 API 信封、Policy、队列 Job、PHPUnit/Pest 测试模板一并呈现可直接复制到项目根目录作为团队基线。说明当前仓库以该技能文档含 docs/es/skills/laravel-patterns/SKILL.md、skills/laravel-patterns/SKILL.md 及多语言镜像作为交付物本身并不包含可运行的 Laravel 应用源码文中所列 PHP 代码均为技能定义的最佳实践示例落地前请结合目标项目的 Laravel 主版本与既有约定适配。以上从目录分层、依赖注入、Eloquent 数据访问、迁移、校验与序列化到异步处理与缓存配置构成一套完整可执行的 Laravel 生产级架构基线。把本文的原则落实为团队编码规范配合 ECC 的审查代理与模板项目即可让 Laravel 代码在可维护性、安全性与查询性能上保持一致的高水位。【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表