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