PHP群:95885625 Hbuilder+MUI群:81989597 站长QQ:634381967
    您现在的位置: 首页 > 开发编程 > Laravel教程 > 正文

    Laravel 如何使用 Repository 模式?

    作者:admin来源:网络浏览:时间:2020-09-30 00:07:50我要评论
    导读:若将资料库逻辑都写在 model,会造成 model 的肥大而难以维护,基於SOLID原则,我们应该使用 Repository 模式辅助 model,将相关的资...
    若将资料库逻辑都写在 model,会造成 model 的肥大而难以维护,基於SOLID原则,我们应该使用 Repository 模式辅助 model,将相关的资料库逻辑封装在不同的 repository,方便中大型专案的维护。

    资料库逻辑
     
    在 CRUD 中,CUD 比较稳定,但 R 的部分则千变万化,大部分的资料库逻辑都在描述 R 的部分,若将资料库逻辑写在 controller 或 model 都不适当,会造成 controller 与 model 肥大,造成日后难以维护。

    Model
     
    使用 repository 之后,model 仅当成Eloquent class 即可,不要包含资料库逻辑,仅保留以下部分 :
     
    Property : 如$table,$fillable…等。
    Mutator: 包括 mutator 与 accessor。
    Method : relation 类的 method,如使用 hasMany() 与 belongsTo()。
    註解 : 因为 Eloquent 会根据资料库栏位动态產生 property 与 method,等。若使用 Laravel IDE Helper,会直接在 model 加上 @property 与 @method 描述 model 的动态 property 与 method。

    Repository模式

    初学者常会在 controller 直接调用 model 写资料库逻辑 :

    1. public function index() 
    2.     $users = User::where('age''>', 20) 
    3.                 ->orderBy('age'
    4.                 ->get(); 
    5.  
    6.     return view('users.index', compact('users')); 
    资料库逻辑是要抓 20 岁以上的资料。
     
    在中大型专案,会有几个问题 :
     
    将资料库逻辑写在 controller,造成 controller 的肥大难以维护。
    违反 SOLID 的单一职责原则 : 资料库逻辑不应该写在 controller。
    controller 直接相依於model,使得我们无法对 controller 做单元测试。
    比较好的方式是使用 repository :
     
    将 model 依赖注入到 repository。
    将资料库逻辑写在 repository。
    将 repository 依赖注入到 service。
     
    UserRepository.php

    1. namespace MyBlog\Repositories; 
    2.  
    3. use Doctrine\Common\Collections\Collection; 
    4. use MyBlog\User; 
    5.  
    6. class UserRepository 
    7.     /** @var User 注入的User model */ 
    8.     protected $user
    9.  
    10.     /** 
    11.      * UserRepository constructor. 
    12.      * @param User $user 
    13.      */ 
    14.     public function __construct(User $user
    15.     { 
    16.         $this->user = $user
    17.     } 
    18.  
    19.     /** 
    20.      * 回传大于?年纪的资料 
    21.      * @param integer $age 
    22.      * @return Collection 
    23.      */ 
    24.     public function getAgeLargerThan($age
    25.     { 
    26.         return $this->user 
    27.             ->where('age''>'$age
    28.             ->orderBy('age'
    29.             ->get(); 
    30.     } 
    第 8 行

    1. /** @var User 注入的User model */ 
    2. protected $user
    3.  
    4. /** 
    5.  * UserRepository constructor. 
    6.  * @param User $user 
    7.  */ 
    8. public function __construct(User $user
    9.     $this->user = $user
    将相依的 User model 依赖注入到 UserRepository。
     
    21 行

    1. /** 
    2.  * 回传大于?年纪的资料 
    3.  * @param integer $age 
    4.  * @return Collection 
    5.  */ 
    6. public function getAgeLargerThan($age
    7.     return $this->user 
    8.         ->where('age''>'$age
    9.         ->orderBy('age'
    10.         ->get(); 
    将抓 20 岁以上的资料的资料库逻辑写在 getAgeLargerThan()。
     
    不是使用User facade,而是使用注入的$this->user。3
    3这裡也可以使用User facade 的方式,并不会影响可测试性,因为实务上在测试 repository 时,会真的去读写资料库,而不会去 mock User model,因此可以依可测试性决定要用依赖注入还是 Facade。
     

     

    UserController.php

    1. namespace App\Http\Controllers; 
    2.  
    3. use App\Http\Requests; 
    4. use MyBlog\Repositories\UserRepository; 
    5.  
    6. class UserController extends Controller 
    7.     /** @var  UserRepository 注入的UserRepository */ 
    8.     protected $userRepository
    9.  
    10.     /** 
    11.      * UserController constructor. 
    12.      * 
    13.      * @param UserRepository $userRepository 
    14.      */ 
    15.     public function __construct(UserRepository $userRepository
    16.     { 
    17.         $this->userRepository = $userRepository
    18.     } 
    19.  
    20.     /** 
    21.      * Display a listing of the resource. 
    22.      * 
    23.      * @return \Illuminate\Http\Response 
    24.      */ 
    25.     public function index() 
    26.     { 
    27.         $users = $this->userRepository 
    28.             ->getAgeLargerThan(20); 
    29.  
    30.         return view('users.index', compact('users')); 
    31.     } 
    第8行

    1. /** @var  UserRepository 注入的UserRepository */ 
    2. protected $userRepository
    3.  
    4. /** 
    5.  * UserController constructor. 
    6.  * 
    7.  * @param UserRepository $userRepository 
    8.  */ 
    9. public function __construct(UserRepository $userRepository
    10.     $this->userRepository = $userRepository
    将相依的 UserRepository 依赖注入到 UserController。

    26行

    1. /** 
    2.  * Display a listing of the resource. 
    3.  * 
    4.  * @return \Illuminate\Http\Response 
    5.  */ 
    6. public function index() 
    7.     $users = $this->userRepository 
    8.         ->getAgeLargerThan(20); 
    9.  
    10.     return view('users.index', compact('users')); 
    从原本直接相依的 User model,改成依赖注入的 UserRepository。

    改用这种写法,有几个优点 :
    将资料库逻辑写在 repository,解决 controller 肥大问题。
    符合 SOLID 的单一职责原则 : 资料库逻辑写在 repository,没写在 controller。
    符合 SOLID 的依赖反转原则 : controller 并非直接相依於 repository,而是将 repository 依赖注入进 controller。
     实务上建议 repository 仅依赖注入於 service,而不要直接注入在 controller,本范例因为还没介绍到 servie 模式,为了简化起见,所以直接注入於 controller。

    是否该建立 Repository Interface?
    理论上使用依赖注入时,应该使用 interface,不过 interface 目的在於抽象化方便抽换,让程式码达到开放封闭的要求,但是实务上要抽换 repository 的机会不高,除非你有抽换资料库的需求,如从 MySQL 抽换到 MongoDB,此时就该建立 repository interface。
     
    不过由於我们使用了依赖注入,将来要从 class 改成 interface 也很方便,只要在 constructor 的 type hint 改成 interface 即可,维护成本很低,所以在此大可使用 repository class 即可,不一定得用 interface 而造成 over design,等真正需求来时再重构成 interface 即可。

    转载请注明(B5教程网)原文链接:https://b5.mxunkeji.com/content-153-6145-1.html
    相关热词搜索: