repository 模式是 laravel 中主动引入的解耦手段,需明确分离数据获取与业务逻辑,仅在多处复用、切换数据源、需 mock 测试或控制器臃肿时引入;接口应聚焦业务语义而非数据库操作,通过服务容器显式绑定,并避免在 repository 中处理事务、缓存等横切关注点。

Repository 模式在 Laravel 中不是框架内置的强制规范,而是你主动引入的解耦手段;它真正起作用的前提是——你已明确区分「数据获取」和「业务逻辑」,且控制器里不再直接调用 Eloquent 模型方法(比如 User::where(...)->get())。
什么时候该加 Repository?
别一上来就为每个模型建 Repository。真实项目中,只在以下场景值得引入:
- 同一个数据查询逻辑被 3 个以上控制器/服务复用(比如「获取用户最近 5 条未读通知」)
- 需要在不改业务代码的前提下切换数据源(如从 MySQL 切到 Redis 缓存 + API 聚合)
- 单元测试时想 mock 数据层,但模型方法紧耦合了数据库连接(
DB::connection()或Model::query()) - 团队里有人常把复杂 where、join、with 写进控制器,导致控制器体积膨胀、难以维护
Repository 接口与实现怎么写才不僵硬?
关键不是“写接口”,而是接口方法要聚焦「业务语义」,而不是数据库操作动词。避免定义 findByStatusAndDate() 这种带具体字段名的方法——下次加个 type 条件就得改接口。
推荐写法:
// app/Repositories/OrderRepository.php
interface OrderRepository
{
public function recentUnshipped(int $limit = 10): Collection;
public function byCustomer(string $customerId): Collection;
public function countByStatus(string $status): int;
}
对应实现里才用 Eloquent 构建查询:
// app/Repositories/EloquentOrderRepository.php
class EloquentOrderRepository implements OrderRepository
{
public function recentUnshipped(int $limit = 10): Collection
{
return Order::where('status', 'pending')
->orderByDesc('created_at')
->take($limit)
->get();
}
}
注意:Collection 是返回类型提示,不是强制约束;别为了类型安全把方法拆得太碎,反而增加调用方负担。
依赖注入怎么配才不踩坑?
Laravel 的服务容器能自动绑定接口和实现,但容易漏掉两件事:
- 在
AppServiceProvider::register()里显式绑定,否则接口无法解析:$this->app->bind(OrderRepository::class, EloquentOrderRepository::class); - 别在 Repository 实现类构造函数里直接 new 模型实例(如
new Order()),应通过容器解析或传入模型类名——否则单元测试时无法替换模型行为 - 如果 Repository 需要分页,不要返回
LengthAwarePaginator(它强依赖 HTTP 请求上下文),改用paginate()返回原始分页对象,让控制器决定如何响应(JSON 还是 Blade)
最常被忽略的一点:Repository 不解决事务、缓存、搜索这些事。它们属于更高层的服务(Service)职责。把 cache()->remember() 塞进 Repository 方法里,看似省事,实则污染了「数据访问」的单一性——下次换缓存驱动就得改所有 Repository,而不是只换一个 CacheService。










