直接在 Controller 調用 Eloquent 模型(House::all())雖然方便,但導致控制器與 ORM 緊耦合,單元測試難、切換數據源難、業務邏輯分散。引入 Repository Pattern,通過接口定義數據訪問契約,實現依賴反轉,讓 Controller 僅依賴抽象,不依賴具體實現。
class HousesController extends BaseController {
public function index() {
$houses = House::all(); // 緊耦合 Eloquent
return View::make('houses.index', compact('houses'));
}
public function show($id) {
$house = House::find($id);
return View::make('houses.show', compact('house'));
}
}
app/
Repositories/
Contracts/ # 接口
HouseRepositoryInterface.php
Eloquent/ # Eloquent 實現
EloquentHouseRepository.php
Providers/
RepositoryServiceProvider.php
// app/Repositories/Contracts/HouseRepositoryInterface.php
namespace App\Repositories\Contracts;
interface HouseRepositoryInterface {
public function all();
public function find($id);
public function getPopular($limit = 10); // 業務專用方法
}
// app/Repositories/Eloquent/EloquentHouseRepository.php
namespace App\Repositories\Eloquent;
use App\Models\House;
use App\Repositories\Contracts\HouseRepositoryInterface;
class EloquentHouseRepository implements HouseRepositoryInterface {
public function all() {
return House::all();
}
public function find($id) {
return House::find($id);
}
public function getPopular($limit = 10) {
return House::where('views', '>', 1000)
->orderBy('views', 'desc')
->take($limit)
->get();
}
}
// app/Repositories/Providers/RepositoryServiceProvider.php
namespace App\Repositories\Providers;
use Illuminate\Support\ServiceProvider;
use App\Repositories\Contracts\HouseRepositoryInterface;
use App\Repositories\Eloquent\EloquentHouseRepository;
class RepositoryServiceProvider extends ServiceProvider {
public function register() {
$this->app->bind(
HouseRepositoryInterface::class,
EloquentHouseRepository::class
);
}
}
在 config/app.php 的 providers 陣列末尾添加:
App\Repositories\Providers\RepositoryServiceProvider::class,
// app/Http/Controllers/HousesController.php
namespace App\Http\Controllers;
use App\Repositories\Contracts\HouseRepositoryInterface;
class HousesController extends Controller {
protected $houseRepo;
public function __construct(HouseRepositoryInterface $houseRepo) {
$this->houseRepo = $houseRepo;
}
public function index() {
$houses = $this->houseRepo->all();
return view('houses.index', compact('houses'));
}
public function show($id) {
$house = $this->houseRepo->find($id);
return view('houses.show', compact('house'));
}
public function popular() {
$houses = $this->houseRepo->getPopular(5);
return view('houses.popular', compact('houses'));
}
}
$this->app->instance(HouseRepositoryInterface::class, $mockRepo); 無需數據庫。CacheHouseRepository、ApiHouseRepository 實現同一接口,服務提供者改一行綁定即可全局切換。laravel-repository 等擴展包提供生成器、Criteria 模式等增強。Repository 模式不是銀彈,但對中大型項目是「架構分層」的關鍵一步。從今天開始,給核心模型加上接口與 Repository,你的代碼將更乾淨、更自由。