如何在 Laravel 中通过 Repository 模式解耦产品创建逻辑

3次阅读

如何在 Laravel 中通过 Repository 模式解耦产品创建逻辑

本文详解如何将控制器中的产品创建逻辑抽离为独立的 repository 类,通过依赖注入和接口绑定实现松耦合、可测试、易维护的代码结构。

本文详解如何将控制器中的产品创建逻辑抽离为独立的 repository 类,通过依赖注入和接口绑定实现松耦合、可测试、易维护的代码结构。

laravel 应用开发中,将业务逻辑(如数据创建、查询、更新)硬编码在控制器中会导致代码臃肿、难以复用、不利于单元测试,也违背了单一职责原则。以 Product 模型的创建为例,原始写法直接在 store() 方法中调用 Product::create(),虽简洁但缺乏可扩展性与可维护性。推荐采用 Repository 模式——即定义清晰接口、实现具体逻辑、通过依赖注入解耦调用方,从而实现关注点分离。

✅ 正确实践:构建 ProductRepository 体系

1. 创建接口与实现类

首先生成接口和仓库类(建议统一放在 app/Repositories 目录下):

php artisan make:Interface Repositories/ProductRepositoryInterface php artisan make:class Repositories/ProductRepository

⚠️ 注意:Laravel 不内置 make:interface 命令,可手动创建;或使用社区包(如 laravel-artisan-commands),但手动创建更可控。

app/Repositories/ProductRepositoryInterface.php

<?php  namespace AppRepositories;  use AppModelsProduct;  interface ProductRepositoryInterface {     /**      * 创建新产品记录      *      * @param  array  $data  包含 name、en_name、type、cat_id 等字段的关联数组      * @return Product  新建的模型实例      */     public function createProduct(array $data): Product; }

app/Repositories/ProductRepository.php

<?php  namespace AppRepositories;  use AppModelsProduct;  class ProductRepository implements ProductRepositoryInterface {     protected Product $product;      public function __construct(Product $product)     {         $this->product = $product;     }      public function createProduct(array $data): Product     {         // 可在此添加统一的数据清洗、权限校验、默认值填充等逻辑         return $this->product->create($data);     } }

? 提示:继承 BaseRepository 并非必需;若项目中存在通用 CRUD 抽象,再考虑基类;初学者建议从具体实现入手,避免过早抽象。

2. 注册服务容器绑定

创建服务提供者以声明接口与实现的绑定关系:

php artisan make:provider RepositoryServiceProvider

编辑 app/Providers/RepositoryServiceProvider.php 的 register() 方法:

public function register() {     $this->app->bind(         AppRepositoriesProductRepositoryInterface::class,         AppRepositoriesProductRepository::class     ); }

然后在 config/app.php 的 providers 数组中注册该提供者:

AppProvidersRepositoryServiceProvider::class,

3. 在 Controller 中依赖注入使用

修改控制器方法签名,自动解析仓库实例:

<?php  namespace ApphttpControllers;  use AppHttpControllersController; use AppRepositoriesProductRepositoryInterface; use IlluminateHttpRequest;  class ProductController extends Controller {     public function store(Request $request, ProductRepositoryInterface $productRepo)     {         // 验证请求数据(强烈建议前置验证)         $validated = $request->validate([             'product_name'       => 'required|string|max:255',             'product_name_english' => 'nullable|string|max:255',             'product_type'       => 'required|string|max:100',             'category_product'   => 'required|exists:categories,id',         ]);          // 映射请求字段到数据库字段(保持语义清晰)         $productData = [             'name'      => $validated['product_name'],             'en_name'   => $validated['product_name_english'] ?? null,             'type'      => $validated['product_type'],             'cat_id'    => $validated['category_product'],         ];          $newProduct = $productRepo->createProduct($productData);          return response()->json(['message' => 'Product created', 'data' => $newProduct], 201);     } }

? 关键优势与注意事项

  • 可测试性强:可轻松 Mock ProductRepositoryInterface,隔离测试控制器行为;
  • 逻辑复用性高:同一 createProduct() 方法可在命令行、API、后台任务等多处调用;
  • 便于演进:未来若需切换为队列创建、审计日志、多库写入等,仅需修改 Repository 实现,控制器零改动;
  • ⚠️ 勿滥用 Facade:本例不推荐自定义 Facade——它会隐藏依赖、增加测试难度;Laravel 官方也倡导“面向接口编程 + 构造器注入”;
  • ⚠️ 验证应前置:务必在调用 Repository 前完成请求验证(如 validate() 或 Form Request),确保传入数据合法;
  • ⚠️ 命名一致性:接口名建议为 XxxRepositoryInterface,实现类为 XxxRepository,避免混淆(原答案中误将 CategoryRepositoryInterface 用于 Product,已修正)。

通过以上重构,你的 ProductController@store 不再承担数据持久化职责,而是专注处理 HTTP 协议层逻辑(接收、响应、跳转),真正实现分层清晰、职责明确的现代 Laravel 架构

text=ZqhQzanResources