首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

Laravel Repository 模式實戰:解耦 Controller 與 Eloquent,打造可測試、可維護的架構

     編輯:PHP 相關知識

Laravel Repository 模式實戰:解耦 Controller 與 Eloquent,打造可測試、可維護的架構

直接在 Controller 調用 Eloquent 模型(House::all())雖然方便,但導致控制器與 ORM 緊耦合,單元測試難、切換數據源難、業務邏輯分散。引入 Repository Pattern,通過接口定義數據訪問契約,實現依賴反轉,讓 Controller 僅依賴抽象,不依賴具體實現。

一、不使用 Repository 的痛點

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'));
  }
}
  • 測試時需建立真實數據庫,無法 Mock
  • 若改用 Redis/文件/外部 API 存儲 House,Controller 全部要改
  • 業務邏輯(如「熱門房源」篩選)散落在各 Controller

二、Repository 模式六步落地

1. 建立目錄結構

app/
  Repositories/
    Contracts/        # 接口
      HouseRepositoryInterface.php
    Eloquent/         # Eloquent 實現
      EloquentHouseRepository.php
    Providers/
      RepositoryServiceProvider.php

2. 定義接口

// app/Repositories/Contracts/HouseRepositoryInterface.php
namespace App\Repositories\Contracts;

interface HouseRepositoryInterface {
  public function all();
  public function find($id);
  public function getPopular($limit = 10); // 業務專用方法
}

3. 實現 Eloquent 版本

// 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();
  }
}

4. 服務提供者綁定接口與實現

// 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
    );
  }
}

5. 註冊服務提供者

在 config/app.php 的 providers 陣列末尾添加:

App\Repositories\Providers\RepositoryServiceProvider::class,

6. Controller 依賴注入接口

// 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 實現同一接口,服務提供者改一行綁定即可全局切換。
  • 業務邏輯集中:複雜查詢、緩存策略、數據轉換封裝在 Repository,Controller 保持輕量。
  • 團隊協作:接口定義即契約,前後端/後端並行開發。

四、常見誤區與建議

  1. 不要過度設計:簡單 CRUD 無業務邏輯的模型可直接用 Eloquent,Repository 針對核心聚合根。
  2. 接口粒度:按聚合根劃分(如 OrderRepository、ProductRepository),而非按表。
  3. 避免在 Repository 返回 View/Response:只返回集合/模型/DTO,保持層純淨。
  4. Laravel 內置 Repository?:框架未內置,但 laravel-repository 等擴展包提供生成器、Criteria 模式等增強。

Repository 模式不是銀彈,但對中大型項目是「架構分層」的關鍵一步。從今天開始,給核心模型加上接口與 Repository,你的代碼將更乾淨、更自由。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved