翻译 Laravel Repository Pattern

[toc]

Repository 模式对你非常有帮助,可以使你的代码更加简洁和更具可读性。事实上,你不必使用 Laravel 才能使用这种特定的设计模式。但在这一集中,我们将用 面向对象的 php 框架 Laravel 来展示怎么通过使用 repositories 让我们的控制器不那么冗长,更松散的耦合度,更易读。让我们进入吧!

Working Without Repositories

使用 repositories 不是强制性的!你可以在不使用这种模式在应用中完成很多很棒的的工作,但是,随着时间的推移,你可能会是自己陷入困境。例如,如果不选择使用 repositories,你的应用不容测试并且更换实现类时会很麻烦。让我们看一个例子。

Getting House Listings From a Real Estate Database

HousesController.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php

class HousesController extends BaseController {


public function index()
{
$houses = House::all();

return View::make('houses.index', compact('houses'));
}


public function create()
{
return View::make('houses.create');
}


public function show($id)
{
$house = House::find($id);

return View::make('houses.show', compact('house'));
}
}

用 Eloquent 和数据库交互来保存代售房屋列表时非常典型得代码。它会工作的非常好,但是控制器和 Eloquent 紧密得耦合了。我们可以注入一个 repository 而不是创建一个相同代码的松散耦合度的版本。这种松散的耦合使以后交换实现类更容易。


Working With Repositories

完成整个 repository 模式有相当多的步骤,但是一旦你完成几次,它就会变得很自然。我们将在这里介绍每一步。

  • 1: Create the Repository Folder
    我们最近浏览了你可能使用的常见 Laravel 文件结构。这会在目录中创建一个文件夹 app 来保存所有域特定文件。对于此示例我们将在 app 文件夹创建 repotutrepositories 来包含我们的文件。这也设置了我们的命名空间结构,我们在创建文件时需要记住这一结构。

  • 2: Create Your Interface
    下一步是创建接口类,该接口将确定我们的 repository 必须实现的契约。这只是列出了我们的 repository 中必须呈现的方法。我们的接口类如下所示。请注意我们将使用的名称空间和方法。
    HouseRepositoryInterface.php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?php


    namespace App\repotutrepositories;


    interface HouseRepositoryInterface
    {
    public function selectAll();

    public function find($id);
    }
  • 3: Create Your Repository
    我们现在可以创建 repository,它将为我们完成所有繁重的工作。我们可以在这个文件中放置所有详细的 Eloquent 查询,无论它们变得多么复杂。每个方法只有一个自定义名称,以便在我们的控制器中,我们只需编写一些非常短的代码即可获得期望的结果。再次注意命名空间和use House; 语句。
    DbHouseRepository.php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <?php


    namespace App\repotutrepositories;


    use App\User;
    use Illuminate\Support\Collection;

    class DbHouseRepository implements HouseRepositoryInterface
    {

    public function selectAll()
    {
    $users = new Collection();
    for ($i = 0; $i < 10; $i++) {
    $user = new \stdClass();
    $user->id = $i + 1;
    $user->name = '测试名' . ($i + 1);
    $users->add($user);
    }
    return $users;
    }

    public function find($id)
    {
    return User::find($id);
    }
    }
  • 4: Create Backend Service Provider
    对于我们的控制器,我们将传入一个接口类的提示。我们将进行依赖注入,但本质上是通过接口类的方式。这意味着我们需要在 Laravel 注册接口,以便它知道我们想要使用接口类的哪个实现。我们将首先使用 Eloquent 实现,但稍后我们将转向基于文件的实现,以展示如何轻松的使用接口交换实现。到目前为止,我们把服务提供者放置在与其他文件相同的命名空间中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?php


    namespace App\Providers;


    use Illuminate\Support\ServiceProvider;

    class BackendServiceProvider extends ServiceProvider
    {
    public function register()
    {
    $this->app->bind('App\repotutrepositories\HouseRepositoryInterface', 'App\repotutrepositories\DbHouseRepository');
    }
    }

    这段代码基本上是说,当您看到控制器的类型提示是 HouseRepositoryInterface,我们知道您想要使用 DbHouseRepository

  • 5: Update Your Providers Array
    现在我们已经创建了一个新的服务提供者,我们需要将其添加到 app/config/app.php 中的 providers 中的数组中。完成后,它可能看起来像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    'providers' => [

    /*
    * Laravel Framework Service Providers...
    */
    Illuminate\Auth\AuthServiceProvider::class,
    //...omitted...

    // 测试路由 /demo/home/index
    BackendServiceProvider::class
    ],
  • 6: Update Your Controller for Dependency Injection
    我们已经做好了大部分基础工作。我们现在可以更新控制器以方便注入 HouseRepositoryInterface 的实现。这将使我们能够直接在控制器中删除对 Eloquent 的任何调用,并用简单的自定义方法调用替换它们。我们更新后的控制器可能看起来像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <?php


    namespace App\Http\Controllers\Demo;


    use App\Http\Controllers\Controller;
    use App\repotutrepositories\HouseRepositoryInterface;
    use Illuminate\Support\Facades\View;

    class HouseController extends Controller
    {

    protected $house;

    public function __construct(HouseRepositoryInterface $house)
    {
    $this->house = $house;
    }

    public function index()
    {
    $houses = $this->house->selectAll();

    return $houses;

    }
    }

  • 7: Confirm Routes are Correct
    对于此示例,我们只需设置一个路由资源,如下所示:

    1
    Route::get('/demo/home/index', 'Demo\HouseController@index');
  • 8: Update composer.json(版本较高,没必要,直接访问路由即可测试)

让我们在浏览器内测试,访问正确的路由。


Easy To Change Implementations

假设将来你决定 Eloquent 不是你想要在应用中处理数据存储的方式。没问题!由于您已经为使用接口做好了基础工作,因此您可以简单地创建一个新的存储库并更改服务提供者。让我们看看如何交换实现。

  • 1: Create a New Repository
    我们在这里仅使用一个简单的示例。让我们假设这是一个完整的文件系统类,它通过平面文件提供数据存储。在我们的例子中,我们只需放入一些简单的逻辑来测试交换实现。请注意,此类实现与我们之前测试的 Eloquent 版本完全相同的方法。
    FileHouseRepository.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <?php


    namespace App\repotutrepositories;


    use stdClass;

    class FileHouseRepository implements HouseRepositoryInterface
    {

    public function selectAll()
    {
    $houses = new stdClass;

    $house1 = new stdClass;
    $house1->color = 'Olive';

    $house2 = new stdClass;
    $house2->color = 'Yellow';

    $house3 = new stdClass;
    $house3->color = 'Brown';

    $houses = array($house1,$house2,$house3);

    return $houses;
    }

    public function find($id)
    {
    // TODO: Implement find() method.
    }
    }
  • 2: Update Service Provider
    现在在服务提供商中,我们所要做的就是更改一行代码!我们只是告诉 Laravel,现在当你看到 HouseRepositoryInterface 时,你将使用 FileHouseRepository 而不是 DbHouseRepository。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?php


    namespace App\Providers;


    use Illuminate\Support\ServiceProvider;

    class BackendServiceProvider extends ServiceProvider
    {
    public function register()
    {
    // $this->app->bind('App\repotutrepositories\HouseRepositoryInterface', 'App\repotutrepositories\DbHouseRepository');
    $this->app->bind('App\repotutrepositories\HouseRepositoryInterface', 'App\repotutrepositories\FileHouseRepository');
    }
    }

    重新访问路由测试。

Repositories Conclusion

正如您所看到的,让 Repositories 在您的应用中运行似乎需要很多步骤。毫无疑问,涉及几个步骤。然而,如果您要负责长期维护一段代码,那么您将从一开始就花时间正确构建应用中获得好处。将其视为一种延迟满足的形式,在当今时代似乎是一门被遗忘的艺术。


原文:Laravel Repository Pattern


翻译 Laravel Repository Pattern
https://hutaoren.cn/2021/12/19/翻译/翻译 Laravel Repository Pattern/
作者
胡桃仁
发布于
2021年12月19日
许可协议