Laravel - 服務設計模式

PHPSIX發表於2017-11-15
  • 服務模式
    • 在 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中進行注入或引用,要麼在控制其中新增。避免交叉引用亂象!
本作品採用《CC 協議》,轉載必須註明作者和本文連結
老郭部落格:laughing: 個人部落格地址:www.phpsix.com

相關文章