Laravel 中的 Repository Pattern:清理你的混乱代码
发布: (2025年12月28日 GMT+8 19:55)
5 min read
原文: Dev.to
Source: Dev.to

问题
见过这样的控制器吗?
public class OrderController extends Controller
{
public function show($id)
{
$order = Order::with(['customer', 'items.product'])
->where('id', $id)
->first();
return response()->json($order);
}
public function getUserOrders($userId)
{
// Same query duplicated! 😱
$orders = Order::with(['customer', 'items.product'])
->where('customer_id', $userId)
->get();
return response()->json($orders);
}
}
问题
- 🔴 到处都是重复的查询
- 🔴 控制器与 Eloquent 紧密耦合
- 🔴 没有数据库无法进行测试
- 🔴 业务逻辑与数据访问混在一起
解决方案:仓储模式
步骤 1 – 创建接口
interface OrderRepositoryInterface
{
public function find(int $id): ?Order;
public function findWithRelations(int $id): ?Order;
public function findByCustomer(int $customerId): Collection;
}
步骤 2 – 实现仓储
class OrderRepository implements OrderRepositoryInterface
{
protected $model;
public function __construct(Order $model)
{
$this->model = $model;
}
public function findWithRelations(int $id): ?Order
{
return $this->model
->with(['customer', 'items.product'])
->find($id);
}
public function findByCustomer(int $customerId): Collection
{
return $this->model
->with(['customer', 'items.product'])
->where('customer_id', $customerId)
->orderBy('created_at', 'desc')
->get();
}
}
步骤 3 – 在服务提供者中注册
class RepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(
OrderRepositoryInterface::class,
OrderRepository::class
);
}
}
步骤 4 – 精简控制器
class OrderController extends Controller
{
protected $orderRepository;
public function __construct(OrderRepositoryInterface $orderRepository)
{
$this->orderRepository = $orderRepository;
}
public function show(int $id)
{
$order = $this->orderRepository->findWithRelations($id);
if (! $order) {
return response()->json(['message' => 'Not found'], 404);
}
return response()->json($order);
}
public function getUserOrders(int $userId)
{
$orders = $this->orderRepository->findByCustomer($userId);
return response()->json($orders);
}
}
好处
- ✅ 无重复 – 查询逻辑集中在一个地方
- ✅ 易于测试 – 模拟仓库而不是数据库
- ✅ 灵活性 – 在不触及业务逻辑的情况下切换数据源
- ✅ 代码整洁 – 控制器仅关注 HTTP 相关事务
- ✅ 可重用性 – 同一仓库可在控制器、任务、命令等中使用
高级:基础仓库
abstract class BaseRepository
{
protected $model;
public function all(): Collection
{
return $this->model->all();
}
public function find(int $id): ?Model
{
return $this->model->find($id);
}
public function create(array $data): Model
{
return $this->model->create($data);
}
public function update(int $id, array $data): bool
{
return $this->model->find($id)?->update($data) ?? false;
}
public function delete(int $id): bool
{
return $this->model->find($id)?->delete() ?? false;
}
}
扩展基础仓库
class ProductRepository extends BaseRepository
{
public function __construct(Product $model)
{
parent::__construct($model);
}
public function getFeatured(int $limit = 10): Collection
{
return $this->model
->where('is_featured', true)
->where('stock', '>', 0)
->limit($limit)
->get();
}
public function searchAndFilter(array $filters)
{
$query = $this->model->query();
if (!empty($filters['search'])) {
$query->where('name', 'like', "%{$filters['search']}%");
}
if (!empty($filters['min_price'])) {
$query->where('price', '>=', $filters['min_price']);
}
return $query->paginate(15);
}
}
轻松进行测试
不使用仓库
// Must set up entire database
$order = Order::factory()->hasItems(3)->create();
$response = $this->getJson("/api/orders/{$order->id}");
使用仓库
// Just mock the repository!
$orderRepo = Mockery::mock(OrderRepositoryInterface::class);
$orderRepo->shouldReceive('find')
->with(1)
->andReturn($mockOrder);
$this->app->instance(OrderRepositoryInterface::class, $orderRepo);
常见陷阱
❌ 不要返回查询构建器
// 错误
public function getActive()
{
return $this->model->where('active', true); // Query builder!
}
✅ 返回具体结果
// 正确
public function getActive(): Collection
{
return $this->model->where('active', true)->get();
}
✅ 仓库仅用于数据访问
// 正确 – 仓库仅处理数据
public function create(array $data): Order
{
return $this->model->create($data);
}
// 业务逻辑在 Service 中
class OrderService
{
public function placeOrder(array $data): Order
{
$order = $this->orderRepository->create($data);
Mail::to($order->customer)->send(new OrderCreated($order));
return $order;
}
}
快速检查清单
- 我的控制器是否直接进行数据库查询?
- 我是否在多个地方重复相同的查询?
- 在没有数据库的情况下测试我的代码是否困难?
- 我是否想轻松在 Eloquent / Query Builder / 原始 SQL 之间切换?
- 我是否在构建一个不仅仅是简单 CRUD 的应用?
如果你对 2 条以上的问题回答了 yes,仓储模式将帮助你!
结论
Repository 模式并非对所有简单的 CRUD 应用都必需,但当你的应用规模扩大时,它就变得极其宝贵。它为你提供:
- 干净、可测试的代码
- 集中的数据访问逻辑
- 更改数据源的灵活性
- 更好的关注点分离
从小处开始——先在最复杂的模型上实现它,然后根据需要逐步扩展。
想要完整深度解析?
这是精简版!想阅读完整指南,包括:
- ✨ 更多高级示例(缓存、服务层集成)
- ✨ 实际博客系统实现
- ✨ 完整的测试策略
- ✨ 电商订单管理示例
阅读 Medium 上的完整文章:
👉 Repository Pattern in Laravel: From Problem to Solution
关注我获取更多 Laravel 小技巧:
你对 Repository Pattern 有什么经验?喜欢它吗?讨厌它吗?在评论区一起讨论吧! 💬
