Laravel - 服務設計模式


服務設計模式 - start

  • 服務模式
    • 在 app 目錄下建立一個 Repositories 目錄,在 Repositories 目錄下,創建三個子目錄:Contracts、Eloquent 和 Exceptions。
      Contracts: 接口目錄
      Eloquent: 用於存放實現 Repository 接口的抽象類和具體類。
      Exceptions:目錄用於存放異常處理類。
      file

      這種模式是參考 Laravel 的服務容器和服務提供者,Laravel 服務容器是一個用於管理類依賴和執行依賴注入的強大工具。依賴注入聽上去很花哨,其實質是通過構造函數或者某些情況下通過 set 方法將類依賴注入到類中。服務提供者是所有 Laravel 應用啟動的中心,你自己的應用以及所有 Laravel 的核心服務都是通過服務提供者啟動。通過服務容器創建服務 (service),然后在服務提供者中 (provider) 注冊。
    • 實現步驟:
      1. 定義接口
        在 app\Repositories\Contracts 文件夾下創建 UserInterface.php
        <?php
        namespace App\Repositories\Contracts;
        interface UserInterface{
                public function findBy($id);
        }
      2. 實現接口類
        在 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);
                    }
            }
      3. 注冊服務
        創建 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');
        }
      4. 添加到配置文件中:
        注:如果不是自己創建 provider,而是直接在 AppServiceProvider.php 中綁定的話,則不需要操作本步驟,因為系統已經引入了那 4 個 Provider 。
        具體步驟:
        打開 config/app.php, providers 數組中添加下面一行:
        app\Providers\RepositoryServiceProvider::class,
      5. 在控制器中調用
        首先要在控制器中引入:
        use App\Repositories\Contracts\UserInterface;
        然后再進行依賴注入:
        private $user;   //定義一個私有的變量
        public function __construct(UserInterface $user){
            $this->user = $user;
        }
        public function index(){
            dd($this->user->findBy(1)->toArray());
        }
  • 門面模式

    • 門面為應用的服務容器中的綁定類提供了一個 “靜態” 接口。Laravel 內置了很多門面,你可能在不知道的情況下正在使用它們。Laravel 的門面作為服務容器中的底層類的 “靜態代理”,相比於傳統靜態方法,在維護時能夠提供更加易於測試、更加靈活的、簡明且富有表現力的語法。門面就是一個提供訪問容器中對象的類。該機制原理由 Facade 類實現,Laravel 自帶的門面,以及創建的自定義門面,都會繼承自 Illuminate\Support\Facades\Facade 基類。門面類只需要實現一個方法:getFacadeAccessor。正是 getFacadeAccessor 方法定義了從容器中解析什么,然后 Facade 基類使用魔術方法 __callStatic () 從你的門面中調用解析對象。
    • 實現步驟:

      1. 定義接口(建議):
        雖然門面模式中可以不用定義接口,個人建議還是定義接口。在以后的項目功能升級並兼容老功能的時候非常有用,增加了開發時間,卻減少了后期維護或升級功能的難度。
        具體步驟:
        在 app\Repositories\Contracts 文件夾下新建 UserInterface.php
        <?php
        namespace App\Repositories\Contracts;
        interface UserInterface{
            public function findBy($id);
        }
      2. 接口實現類
        具體步驟:
        在 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);
                }
        }
      3. 注冊服務
        執行如下命令,新建一個 RepositoryServiceProvider:
        php artisan make:provider RepositoryServiceProvider
        在 register 方法中添加如下代碼來注冊服務:
        $this->app->singleton('UserFacadeRepository',function ($app){
                return new \App\Repositories\Eloquent\UserFacadeRepository();
        });
      4. 定義 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';
                }
        }
      5. 在 app\config\app.php 中的 aliases 數組中添加下面代碼:
        //自己新建的門面
        'UserRepository' => App\Facades\UserFacade::class,
      6. 使用門面
        首先在方法中引入門面:
        use UserRepository;
        使用門面:
        public function index(){
                dd(UserRepository::findBy(1)->toArray());
        }
  • 倉庫(Repository)模式

    • Repository 是銜接數據映射層和領域層之間的一個紐帶,作用相當於一個在內存中的域對象集合。客戶端對象把查詢的一些實體進行組合,並把它們提交給 Repository。對象能夠從 Repository 中移除或者添加。Repository 是 MVC 中銜接 Controller 和 Model 之間的一個紐帶。從概念上講,Repository 是把將數據給封裝后的集合並提供給 Controller 的操作。
    • 實現步驟:

      1. 定義接口
        在 app\Repositories\Contracts 目錄下新建一個 UserInterface.php
          <?php
            namespace App\Repositories\Contracts;
            interface UserInterface{
                    public function findBy($id);
            }
      2. 定義一個基本的抽象類:
        在 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;
                    }
            }
      3. 創建 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(); } }
      4. 控制器依賴注入
        首先需要引入文件,然后在 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');
                }
        }
  • 總結:
  • 這種將數據訪問從業務邏輯中分離出來的模式有很多好處:
    1. 集中數據訪問邏輯使代碼易於維護
    2. 業務和數據訪問邏輯完全分離
    3. 減少重復代碼
    4. 使程序出錯的幾率降低
  • 一個控制器調用多個 Repository 數據的時候,要么都在相應的 Repository 中進行注入或引用,要么在控制其中添加。避免交叉引用亂象!
 

服務設計模式 - end

老郭博客:laughing:
個人博客地址:https://www.phpsix.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM