翻译 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 |
|
用 Eloquent 和数据库交互来保存代售房屋列表时非常典型得代码。它会工作的非常好,但是控制器和 Eloquent 紧密得耦合了。我们可以注入一个 repository 而不是创建一个相同代码的松散耦合度的版本。这种松散的耦合使以后交换实现类更容易。
Working With Repositories
完成整个 repository 模式有相当多的步骤,但是一旦你完成几次,它就会变得很自然。我们将在这里介绍每一步。
1: Create the Repository Folder
我们最近浏览了你可能使用的常见 Laravel 文件结构。这会在目录中创建一个文件夹app
来保存所有域特定文件。对于此示例我们将在app
文件夹创建repotutrepositories
来包含我们的文件。这也设置了我们的命名空间结构,我们在创建文件时需要记住这一结构。2: Create Your Interface
下一步是创建接口类,该接口将确定我们的 repository 必须实现的契约。这只是列出了我们的 repository 中必须呈现的方法。我们的接口类如下所示。请注意我们将使用的名称空间和方法。
HouseRepositoryInterface.php1
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.php1
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.php1
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 在您的应用中运行似乎需要很多步骤。毫无疑问,涉及几个步骤。然而,如果您要负责长期维护一段代码,那么您将从一开始就花时间正确构建应用中获得好处。将其视为一种延迟满足的形式,在当今时代似乎是一门被遗忘的艺术。