Problem
Aşağıdaki gibi controller’lar gördünüz mü?
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)
{
// Aynı sorgu tekrar edilmekte! 😱
$orders = Order::with(['customer', 'items.product'])
->where('customer_id', $userId)
->get();
return response()->json($orders);
}
}
Problemler:
🔴 Her yerde kopyalanmış sorgular
🔴 Controllers Eloquent ile sıkı bir şekilde bağlı
🔴 Veritabanı olmadan test etmek imkansız
🔴 İş mantığı veri erişimiyle karışmış
Çözüm: Repository Pattern
Adım 1: Arayüz Oluştur
interface OrderRepositoryInterface
{
public function find(int $id): ?Order;
public function findWithRelations(int $id): ?Order;
public function findByCustomer(int $customerId): Collection;
}
Adım 2: Repository’i Uygula
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();
}
}
Adım 3: Servis Sağlayıcıda Kayıt
class RepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(
OrderRepositoryInterface::class,
OrderRepository::class
);
}
}
Adım 4: Controller’ı Temizle
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' => 'Bulunamadı'], 404);
}
return response()->json($order);
}
public function getUserOrders(int $userId)
{
$orders = $this->orderRepository->findByCustomer($userId);
return response()->json($orders);
}
}
Faydalar
✅ Kopyalama yok – Sorgu mantığı bir yerde
✅ Kolay test etme – Veritabanı yerine mock repository kullanma
✅ Esneklik – İş mantığını değiştirmeden veri kaynaklarını değiştirme
✅ Temiz kod – Controller’lar HTTP ile ilgili konulara odaklanır
✅ Yeniden kullanılabilirlik – Aynı repository’i controller’larda, görevlerde, komutlarda kullanma
İleri Seviye: Temel Repository
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;
}
}
Şimdi uzat:
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(, '>=', $filters[]);
}
return $query->paginate(15);
}
}
Test Etmeyi Kolaylaştırma
Repository olmadan:
// Tüm veritabanını ayarlamak zorundasınız
$order = Order::factory()->hasItems(3)->create();
$response = $this->getJson("/api/orders/{$order->id}");
Repository ile:
// Sadece repository'yi mock et!
$orderRepo = Mockery::mock(OrderRepositoryInterface::class);
$orderRepo->shouldReceive('find')
->with(1)
->andReturn($mockOrder);
$this->app->instance(OrderRepositoryInterface::class, $orderRepo);
Sık Karşılaşılan Hatalar
❌ Sorgu Oluşturucu Dönmeyin
// Kötü
public function getActive()
{
return $this->model->where('active', true); // Sorgu oluşturucu!
}
✅ Spesifik Sonuçlar Dön
// İyi
public function getActive(): Collection
{
return $this->model->where('active', true)->get();
}
✅ Repository’i Sadece Veri Erişimi İçin Tutun
// İyi - Repository sadece veri ile ilgileniyor
public function create(array $data): Order
{
return $this->model->create($data);
}
// İş mantığı Serviste
class OrderService
{
public function placeOrder(array $data): Order
{
$order = $this->orderRepository->create($data);
Mail::to($order->customer)->send(new OrderCreated($order));
return $order;
}
}
Hızlı Kontrol Listesi
Repository Pattern’i uygulamadan önce kendinize sorun:
- Controller’ım doğrudan veritabanı sorguları mı yapıyor?
- Aynı sorguları birkaç yerde mi kopyalıyorum?
- Kodumu test etmek veritabanı olmadan zor mu?
- Eloquent/Query Builder/Raw SQL arasında kolayca geçiş yapmak mı istiyorum?
- Basit bir CRUD uygulamasından fazlasını mı inşa ediyorum?
Eğer 2 ya da daha fazla soruya evet yanıtı verdiyseniz, Repository Pattern size yardımcı olacaktır!
Sonuç
Repository Pattern’ı basit CRUD uygulamaları için her zaman gerekli değildir, ancak uygulamanız büyüdüğünde çok değerli hale gelir. Size:
Temiz, test edilebilir kod
Merkezi veri erişim mantığı
Veri kaynaklarını değiştirme esnekliği
İyi bir sorumluluk ayrımı
Küçük başlayın – ilk olarak en karmaşık modelleriniz için bunu uygulayın, sonra gerektiğinde genişletin.
Tam Derinlemesine Bir Araştırma mı İstiyorsunuz?
Bu, kısaltılmış bir versiyon! Tam kılavuzu daha fazla gelişmiş örnekler (önbellekleme, servis katmanı entegrasyonu), gerçek dünya blog sistemi uygulaması, tam test stratejileri ve e-ticaret sipariş yönetimi örneği ile okuyabilirsiniz:
👉 Repository Pattern in Laravel: From Problem to Solution
Daha fazla Laravel ipuçları için beni takip edin:
👉 masteryoflaravel on Medium
Repository Pattern hakkındaki deneyiminiz nedir? Seviyor musunuz? Nefret mi? Yorumlarda tartışalım! 💬
Kaynak: Orijinal Makale


