服務設計模式 - start
- 服務模式
- 在 app 目錄下建立一個 Repositories 目錄,在 Repositories 目錄下,創建三個子目錄:Contracts、Eloquent 和 Exceptions。
Contracts: 接口目錄
Eloquent: 用於存放實現 Repository 接口的抽象類和具體類。
Exceptions:目錄用於存放異常處理類。
這種模式是參考 Laravel 的服務容器和服務提供者,Laravel 服務容器是一個用於管理類依賴和執行依賴注入的強大工具。依賴注入聽上去很花哨,其實質是通過構造函數或者某些情況下通過 set 方法將類依賴注入到類中。服務提供者是所有 Laravel 應用啟動的中心,你自己的應用以及所有 Laravel 的核心服務都是通過服務提供者啟動。通過服務容器創建服務 (service),然后在服務提供者中 (provider) 注冊。 - 實現步驟:
- 定義接口
在 app\Repositories\Contracts 文件夾下創建 UserInterface.php<?php namespace App\Repositories\Contracts; interface UserInterface{ public function findBy($id); }
- 實現接口類
在 app\Repositories\Eloquent 文件夾下創建 UserServiceRepository.php<?php namespace App\Repositories\Eloquent; use App\Repositories\Contracts\UserInterface; use App\User; //服務模式 class UserServiceRepository implements UserInterface{ public function findBy($id) { return User::find($id); } }
- 注冊服務
創建 provider 或在系統的 provider 中注冊 (接口與實現類的綁定):
php artisan make:provider RepositoryServiceProvider
打開app\Providers\RepositoryServiceProvider.php
public function register() { //接口與實現類的兩種綁定模式 //單例模式(個人推薦) $this->app->singleton('App\Repositories\Contracts\UserInterface',function ($app){ return new \App\Repositories\Eloquent\UserServiceRepository(); }); //綁定 // $this->app->bind('App\Repositories\Contracts\UserInterface','App\Repositories\Eloquent\UserServiceRepository'); }
- 添加到配置文件中:
注:如果不是自己創建 provider,而是直接在 AppServiceProvider.php 中綁定的話,則不需要操作本步驟,因為系統已經引入了那 4 個 Provider 。
具體步驟:
打開 config/app.php, providers 數組中添加下面一行:
app\Providers\RepositoryServiceProvider::class,
- 在控制器中調用
首先要在控制器中引入:
use App\Repositories\Contracts\UserInterface;
private $user; //定義一個私有的變量 public function __construct(UserInterface $user){ $this->user = $user; } public function index(){ dd($this->user->findBy(1)->toArray()); }
- 定義接口
- 在 app 目錄下建立一個 Repositories 目錄,在 Repositories 目錄下,創建三個子目錄:Contracts、Eloquent 和 Exceptions。
-
門面模式
- 門面為應用的服務容器中的綁定類提供了一個 “靜態” 接口。Laravel 內置了很多門面,你可能在不知道的情況下正在使用它們。Laravel 的門面作為服務容器中的底層類的 “靜態代理”,相比於傳統靜態方法,在維護時能夠提供更加易於測試、更加靈活的、簡明且富有表現力的語法。門面就是一個提供訪問容器中對象的類。該機制原理由 Facade 類實現,Laravel 自帶的門面,以及創建的自定義門面,都會繼承自 Illuminate\Support\Facades\Facade 基類。門面類只需要實現一個方法:getFacadeAccessor。正是 getFacadeAccessor 方法定義了從容器中解析什么,然后 Facade 基類使用魔術方法 __callStatic () 從你的門面中調用解析對象。
-
實現步驟:
- 定義接口(建議):
雖然門面模式中可以不用定義接口,個人建議還是定義接口。在以后的項目功能升級並兼容老功能的時候非常有用,增加了開發時間,卻減少了后期維護或升級功能的難度。
具體步驟:
在 app\Repositories\Contracts 文件夾下新建 UserInterface.php<?php namespace App\Repositories\Contracts; interface UserInterface{ public function findBy($id); }
- 接口實現類
具體步驟:
在 app\Repositories\Eloquent 文件夾下新建 UserFacadeRepository.php<?php namespace App\Repositories\Eloquent; use App\Repositories\Contracts\UserInterface; use App\User; class UserFacadeRepository implements UserInterface{ //根據id查找用戶信息 public function findBy($id){ return User::find($id); } }
- 注冊服務
執行如下命令,新建一個 RepositoryServiceProvider:php artisan make:provider RepositoryServiceProvider
在 register 方法中添加如下代碼來注冊服務:$this->app->singleton('UserFacadeRepository',function ($app){ return new \App\Repositories\Eloquent\UserFacadeRepository(); });
-
定義 Facade(門面)
在 app 目錄下創建一個 Facades 目錄, Facades 目錄下新建一個 UserFacade.php<?php namespace App\Facades; use Illuminate\Support\Facades\Facade; class UserFacade extends Facade{ protected static function getFacadeAccessor(){ return 'UserFacadeRepository'; } }
- 在 app\config\app.php 中的 aliases 數組中添加下面代碼:
//自己新建的門面 'UserRepository' => App\Facades\UserFacade::class,
- 使用門面
首先在方法中引入門面:
use UserRepository;
public function index(){ dd(UserRepository::findBy(1)->toArray()); }
- 定義接口(建議):
-
倉庫(Repository)模式
- Repository 是銜接數據映射層和領域層之間的一個紐帶,作用相當於一個在內存中的域對象集合。客戶端對象把查詢的一些實體進行組合,並把它們提交給 Repository。對象能夠從 Repository 中移除或者添加。Repository 是 MVC 中銜接 Controller 和 Model 之間的一個紐帶。從概念上講,Repository 是把將數據給封裝后的集合並提供給 Controller 的操作。
-
實現步驟:
- 定義接口
在 app\Repositories\Contracts 目錄下新建一個 UserInterface.php<?php namespace App\Repositories\Contracts; interface UserInterface{ public function findBy($id); }
-
定義一個基本的抽象類:
在 app\Repositories\Eloquent 目錄下新建一個 Repository.php<?php namespace App\Repositories\Eloquent; use App\Repositories\Contracts\UserInterface; use Illuminate\Database\Eloquent\Model; use Illuminate\Container\Container as App; abstract class Repository implements UserInterface{ protected $app; //App容器 protected $model; //操作model public function __construct(App $app){ $this->app = $app; $this->makeModel(); } abstract function model(); public function findBy($id){ return $this->model->find($id); } public function makeModel(){ $model = $this->app->make($this->model()); /*是否是Model實例*/ if (!$model instanceof Model){ throw new RepositoryException("Class {$this->model()} must be an instance of Illuminate\\Database\\Eloquent\\Model"); } $this->model = $model; } }
- 創建 UserRepository.php 並繼承抽象類
在 app\Repositories\Eloquent 目錄下新建一個 UserRepository.php<?php namespace App\Repositories\Eloquent; use App\Repositories\Eloquent\Repository; use App\User;
class UserRepository extends Repository{ public function model(){ return User::class; } public function findBy($id){ return $this->model->where('id',$id)->first()->toArray(); } } - 控制器依賴注入
首先需要引入文件,然后在 HomeController 類里書寫代碼:<?php namespace App\Http\Controllers; use App\Http\Requests; use Illuminate\Http\Request; use App\Repositories\Contracts\UserInterface; use UserRepository; use App\Repositories\Eloquent\UserRepository as UserRepo; class HomeController extends Controller{ private $user; //定義一個私有的變量 private $userRepo; public function __construct(UserInterface $user,UserRepo $userRepo){ $this->user = $user; $this->userRepo = $userRepo; } public function index(){ dd($this->userRepo->findBy(2)); return view('home'); } }
- 定義接口
- 總結:
- 這種將數據訪問從業務邏輯中分離出來的模式有很多好處:
- 集中數據訪問邏輯使代碼易於維護
- 業務和數據訪問邏輯完全分離
- 減少重復代碼
- 使程序出錯的幾率降低
- 一個控制器調用多個 Repository 數據的時候,要么都在相應的 Repository 中進行注入或引用,要么在控制其中添加。避免交叉引用亂象!
服務設計模式 - end
老郭博客
個人博客地址:https://www.phpsix.com