Repository模式不是Laravel强制规范,仅在多数据源切换、复杂查询复用超3处、需mock测试或控制器臃肿时引入;接口应聚焦业务语义,通过容器绑定实现依赖注入,避免在Repository中处理事务或缓存。

不推荐盲目使用仓库模式——它只在特定场景下真正有价值,比如项目中出现多数据源切换、复杂查询逻辑频繁变更、或需要对数据访问层做统一缓存/审计/日志时。Laravel 5.5 自带的 Eloquent 已足够应对大多数 CRUD 场景,强行套用仓库反而增加维护成本。
什么时候该写 UserRepositoryInterface 而不是直接用 User::where()
当以下任一条件成立时,才值得引入接口+实现的仓储结构:
- 同一业务逻辑需在不同环境(MySQL / Redis / API)间切换数据源,且不能改控制器代码
- 多个控制器反复拼接相同复杂条件,如
->where('status', 'active')->whereHas('posts', fn($q) => $q->where('published', true)),且该逻辑语义明确(例如“获取已发布内容的活跃用户”) - 需要为某类查询统一加缓存层、审计日志或权限拦截,而这些逻辑不适合塞进模型的本地作用域(
scope)里 - 团队中存在非 PHP 开发者参与接口定义,或你正为未来可能的 DDD 分层做准备
BaseRepository 和手写 UserRepository 的关键区别在哪
BaseRepository(来自 l5-repository 包)是通用抽象,它把 findWhere、skipPresenter 等共性逻辑收拢了,但代价是:
- 所有方法签名被固定,比如
findWhere(array $where)强制你传数组,无法自然表达whereBetween或闭包条件 - 它默认启用“criteria”机制,但 Criteria 类一旦写错(比如漏掉
apply方法),错误提示是Call to undefined method,而非清晰的类型或契约报错 - 它和 Eloquent 的生命周期钩子(
creating、saving)不自动联动,你需要手动在create()中触发模型事件 - 如果你只用它代理基础 CRUD,那和直接写一个空壳
UserRepository类没本质区别,只是多了一层继承
绑定 UserRepositoryInterface 到容器时最容易漏掉的两件事
在 RepositoryServiceProvider@register() 里写 $this->app->bind(UserRepositoryInterface::class, UserRepository::class) 是基础,但实际踩坑点在:
- 没在
config/app.php的providers数组里注册这个服务提供者,导致绑定不生效,DI 失败时报Target [App\Contracts\UserRepositoryInterface] is not instantiable - 接口方法返回类型声明用了 PHP 7.4+ 的
array|Collection,但实现类里忘了加use Illuminate\Support\Collection;,结果运行时报Class 'Collection' not found—— 这种错误不会在 IDE 里标红,只在请求时爆发 - 控制器构造函数参数类型写了
UserRepositoryInterface,但没加use App\Contracts\UserRepositoryInterface;,PHP 解析成当前命名空间下的类,报Class App\Http\Controllers\UserRepositoryInterface does not exist
真正难的从来不是“怎么写完仓库”,而是判断“哪几个模型值得配仓库”。一个新项目里,前 3 个模型几乎都不需要——先让 Eloquent 在控制器里跑通流程,等第二个需求开始复用相同查询逻辑时,再把它抽成仓库,才是合理节奏。


















