Repository Pattern در Laravel — پیادهسازی عمیق و اشتباهات رایج
Repository لایه دسترسی به داده را از business logic جدا میکند. در Laravel بحث داغ است: بعضی میگویند Eloquent خودش repository است؛ بعضی برای تست و swap DB یک interface میخواهند. حقیقت میاندو است — برای پروژه کوچک شاید اضافه؛ برای domain پیچیده و تیم بزرگ ارزش دارد.
ساختار پایه
interface PostRepositoryInterface
{
public function findBySlug(string $slug): ?Post;
public function published(): Collection;
public function create(array $data): Post;
}
class EloquentPostRepository implements PostRepositoryInterface
{
public function findBySlug(string $slug): ?Post
{
return Post::query()->where('slug', $slug)->first();
}
public function published(): Collection
{
return Post::query()
->where('status', 'published')
->orderByDesc('published_at')
->get();
}
public function create(array $data): Post
{
return Post::create($data);
}
}
Binding در ServiceProvider
public function register(): void
{
$this->app->bind(
PostRepositoryInterface::class,
EloquentPostRepository::class
);
}
Service فقط interface میبیند — DIP.
Repository vs Eloquent مستقیم
| مستقیم Eloquent | Repository |
|---|---|
| سریع CRUD | query پیچیده متمرکز |
| کمتر فایل | mock آسان در unit test |
| Laravel idiomatic | swap implementation (cache، read replica) |
Scope در Repository نه Controller
public function forAuthor(User $user): Collection
{
return Post::query()
->where('user_id', $user->id)
->with('category')
->latest()
->get();
}
Scopes داخل repo قابل reuse.
Caching در Repository
public function published(): Collection
{
return Cache::remember('posts.published', 3600, fn () =>
Post::query()->published()->get()
);
}
invalidate در Observer — Observer.
تست با Mock
$repo = Mockery::mock(PostRepositoryInterface::class);
$repo->shouldReceive('findBySlug')->with('test')->andReturn($post);
$this->app->instance(PostRepositoryInterface::class, $repo);
اشتباهات رایج
- Repository که فقط
Post::all()wrap میکند — thin wrapper بیفایده - God Repository برای همه modelها
- business logic داخل Repository — باید در Service/Action باشد
- interface برای هر CRUD ۳ خطی
Generic Repository؟
BaseRepository<T> در PHP کمتر از C# — معمولاً per-aggregate repository واضحتر است.
جمعبندی
Repository وقتی query و data access پیچیده یا تستپذیری مهم است. Action: Action classes. CQRS بعدی در استراتژی.