Laravel Repository Pattern

 

The Repository Pattern can be very helpful to you in order to keep your code a little cleaner and more readable. In fact, you don’t have to be using Laravel in order to use this particular design pattern. For this episode however, we will use the object oriented php framework Laravel to show how using repositories will make our controllers a bit less verbose, more loosely coupled, and easier to read. Let’s jump in!


Working Without Repositories

Using repositories is not mandatory! You can accomplish many great things in your applications without using this pattern, however, over time you may be painting yourself into a corner. For example by choosing not to use repositories, your application is not easily tested and swapping out implementations would be cumbersome. Let’s look at an example.

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'));
}
}

This would be pretty typical code for using Eloquent to interact with the database which holds listings of houses for sale. It will work just fine, but the controller is now tightly coupled to Eloquent. We can inject a repository instead to create a loosely coupled version of the same code. This loose coupling makes it easy to swap implementations at a later time.


Working With Repositories

There are a fair number of steps to complete the entire repository pattern, but once you go through it a few times it becomes second nature. We’re going to cover every step here.

    • 1: Create the Repository Folder

We recently looked at a common Laravel File Structure you might be using. This creates a folder in the app directory to hold all of your domain specific files. For this example we’ll create the repotutrepositories within our app folder to contain our files. This also sets up our namespace structure which we will need to keep in mind for the files we create.

    • 2: Create Your Interface

The next step is to create the interface which will determine the contract our repository must implement. This just lays out the methods that must be present in our repository. Our Interface will look like the following. Note the namespace and methods we will use.

HouseRepositoryInterface.php

 
1
2
3
4
5
6
7
8
9
10
<?php
namespace repotutrepositories;
 
interface HouseRepositoryInterface {
 
public function selectAll();
 
public function find($id);
 
}
    • 3: Create Your Repository

We can now create the repository which will do all of the heavy lifting for us. It is in this file that we can put all of our detailed Eloquent queries, no matter how complex they may become. Each method simply has a custom name so that in our controller, we can just write some very short code to get the desired result. Again note the namespace and the use House; statement.

DbHouseRepository.php

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
namespace repotutrepositories;
 
use House;
 
class DbHouseRepository implements HouseRepositoryInterface {
 
public function selectAll()
{
return House::all();
}
 
public function find($id)
{
return House::find($id);
}
}
    • 4: Create Backend Service Provider

For our controller, we are going to type hint an interface. We are going to be doing dependency injection, but by way of an interface essentially. What this means is that we need to register the interface with Laravel so that it knows which implementation of our interface we want to use. We’ll first use an Eloquent implementation, but later we’ll move to a File based implementation to show how we can swap implementations easily using an interface. We place this Service Provider in the same namespace as our other files so far.

BackendServiceProvider.php

 
1
2
3
4
5
6
7
8
9
10
11
12
<?php
namespace repotutrepositories;
 
use IlluminateSupportServiceProvider;
 
class BackendServiceProvider extends ServiceProvider {
 
public function register()
{
$this->app->bind('repotutrepositoriesHouseRepositoryInterface', 'repotutrepositoriesDbHouseRepository');
}
}

This code basically says, when you see the controller type hinting HouseRepositoryInterface, we know you want to make use of the DbHouseRepository.

    • 5: Update Your Providers Array

Now that we have created a new Service Provider, we need to add this to the providers array within app/config/app.php. It may look something like this once complete:

 
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
'providers' => array(
 
'IlluminateFoundationProvidersArtisanServiceProvider',
'IlluminateAuthAuthServiceProvider',
'IlluminateCacheCacheServiceProvider',
'IlluminateSessionCommandsServiceProvider',
'IlluminateFoundationProvidersConsoleSupportServiceProvider',
'IlluminateRoutingControllerServiceProvider',
'IlluminateCookieCookieServiceProvider',
'IlluminateDatabaseDatabaseServiceProvider',
'IlluminateEncryptionEncryptionServiceProvider',
'IlluminateFilesystemFilesystemServiceProvider',
'IlluminateHashingHashServiceProvider',
'IlluminateHtmlHtmlServiceProvider',
'IlluminateLogLogServiceProvider',
'IlluminateMailMailServiceProvider',
'IlluminateDatabaseMigrationServiceProvider',
'IlluminatePaginationPaginationServiceProvider',
'IlluminateQueueQueueServiceProvider',
'IlluminateRedisRedisServiceProvider',
'IlluminateRemoteRemoteServiceProvider',
'IlluminateAuthRemindersReminderServiceProvider',
'IlluminateDatabaseSeedServiceProvider',
'IlluminateSessionSessionServiceProvider',
'IlluminateTranslationTranslationServiceProvider',
'IlluminateValidationValidationServiceProvider',
'IlluminateViewViewServiceProvider',
'IlluminateWorkbenchWorkbenchServiceProvider',
'repotutrepositoriesBackendServiceProvider'
 
),
    • 6: Update Your Controller for Dependency Injection

We have most of the groundwork in place. We can now update the Controller to facilitate injecting an implementation of the HouseRepositoryInterface. This will all us to remove any calls to Eloquent directly in the Controller, and replace those with simple custom method calls. Our updated controller might look something like this:

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
27
28
29
30
31
32
33
34
35
<?php
 
use repotutrepositoriesHouseRepositoryInterface;
 
class HousesController extends BaseController {
 
public function __construct(HouseRepositoryInterface $house)
{
$this->house = $house;
}
 
 
public function index()
{
$houses = $this->house->selectAll();
 
return View::make('houses.index', compact('houses'));
 
}
 
 
public function create()
{
return View::make('houses.create');
}
 
 
public function show($id)
{
$house = $this->house->find($id);
 
return View::make('houses.show', compact('house'));
 
}
}
    • 7: Confirm Routes are Correct

For this example we simply set up a route resource like so:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
 
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the Closure to execute when that URI is requested.
|
*/
 
Route::resource('houses', 'HousesController');
    • 8: Update composer.json

If you have not done so already, make sure that the namespace we are referencing is in your composer.json. Note the addition of "psr-4":{"repotut\": "app/repotut" } which tells composer to autoload the classes within the repotut namespace.

 
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
35
36
37
38
39
40
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"require": {
"laravel/framework": "4.2.*"
},
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php"
],
            
                "psr-4":{
                     "repotut\": "app/repotut"
                }
},
"scripts": {
"post-install-cmd": [
"php artisan clear-compiled",
"php artisan optimize"
],
"post-update-cmd": [
"php artisan clear-compiled",
"php artisan optimize"
],
"post-create-project-cmd": [
"php artisan key:generate"
]
},
"config": {
"preferred-install": "dist"
},
"minimum-stability": "stable"
}

Don’t forget to run composer dump after updating composer.json!


Whoa! Nice work partner, let’s test it in the browser. We can visit http://localhost/repotut/public/houses and we can see that we have 3 houses for sale, each a different color.


Easy To Change Implementations

Let’s say in the future you decide that Eloquent is not the way you want to handle storing data with your app. No problem! Since you already laid out the ground work for using an interface, you can simple create a new repository and change the Service Provider. Let’s see how we can swap implementations.

    • 1: Create a New Repository

We’ll just use a quick example here. Let’s pretend this is a whole file system class which provides data storage via flat files. In our case, we’ll just put some simple logic in to test swapping implementations. Note that this class implements the same exact methods as the Eloquent version we tested prior.

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
<?php
namespace repotutrepositories;
 
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)
{
return 'Here is a single house listing, again using the file system';
}
}
    • 2: Update Service Provider

Now in the Service Provider, all we have to do is change one single line of code! We simply tell Laravel that now when you see the HouseRepositoryInterface, you will use the FileHouseRepository instead of the DbHouseRepository.

 
1
2
3
4
5
6
7
8
9
10
11
12
<?php
namespace repotutrepositories;
 
use IlluminateSupportServiceProvider;
 
class BackendServiceProvider extends ServiceProvider {
 
public function register()
{
$this->app->bind('repotutrepositoriesHouseRepositoryInterface', 'repotutrepositoriesFileHouseRepository');
}
}

When we test it in the browser at http://localhost/repotut/public/houses we can see that the new implementation has indeed taken effect, very cool!

Repositories Conclusion

As you can see, it seems like a lot of steps to get Repositories working in your application. There are a few steps involved, no doubt about it. If you are going to be responsible for maintaining a piece of code for the long term however, you are going to reap the benefits of taking the time to correctly architect your app in the beginning. Consider it a form of delayed gratification, which in this day and age seems like a forgotten art.

Laravel Repository Pattern的更多相关文章

  1. Laravel与Repository Pattern(仓库模式)

    为什么要学习Repository Pattern(仓库模式) Repository 模式主要思想是建立一个数据操作代理层,把controller里的数据操作剥离出来,这样做有几个好处: 把数据处理逻辑 ...

  2. How To Use The Repository Pattern In Laravel

    The Repository Pattern in Laravel is a very useful pattern with a couple of great uses. The first us ...

  3. Laravel Repository 模式

    Repository 模式 为了保持代码的整洁性和可读性,使用Repository Pattern 是非常有用的.事实上,我们也不必仅仅为了使用这个特别的设计模式去使用Laravel,然而在下面的场景 ...

  4. Follow me to learn what is repository pattern

    Introduction Creating a generic repository pattern in an mvc application with entity framework is th ...

  5. Generic repository pattern and Unit of work with Entity framework

    原文 Generic repository pattern and Unit of work with Entity framework Repository pattern is an abstra ...

  6. Using the Repository Pattern with ASP.NET MVC and Entity Framework

    原文:http://www.codeguru.com/csharp/.net/net_asp/mvc/using-the-repository-pattern-with-asp.net-mvc-and ...

  7. Using Repository Pattern in Entity Framework

    One of the most common pattern is followed in the world of Entity Framework is “Repository Pattern”. ...

  8. 学习笔记之ASP.NET MVC & MVVM & The Repository Pattern

    ASP.NET MVC | The ASP.NET Site https://www.asp.net/mvc ASP.NET MVC gives you a powerful, patterns-ba ...

  9. [转]Using the Repository Pattern with ASP.NET MVC and Entity Framework

    本文转自:http://www.codeguru.com/csharp/.net/net_asp/mvc/using-the-repository-pattern-with-asp.net-mvc-a ...

随机推荐

  1. 数据结构-单链表-类定义2-C++

    上一次的C++链表实现两个单链表的连接不太理想,此次听了一些视频课,自己补了个尾插法,很好的实现了两个链表的连接,当然了,我也是刚接触,可能是C++的一些语法还不太清楚,不过硬是花了一些时间尽量在数据 ...

  2. Bipartite Checking CodeForces - 813F (线段树按时间分治)

    大意: 动态添边, 询问是否是二分图. 算是个线段树按时间分治入门题, 并查集维护每个点到根的奇偶性即可. #include <iostream> #include <sstream ...

  3. O(1) gcd 板子

    const int N = 2e5+10; const int M = 500; int cnt, p[N], _gcd[M][M]; int v[N][3],vis[N]; int gcd(int ...

  4. hdu 1548 简单bfs。。。

    由于题目过水.. 我就在这里把bfs的模板写一些吧.. bfs的思想是利用队列的特性 对树的每一层先遍历 每一次访问时取出队首 然后排出~ #include<queue>void bfs( ...

  5. 在论坛中出现的比较难的sql问题:27(字符串拆分、字符串合并、非连续数字的间隔范围、随机返回字符串)

    原文:在论坛中出现的比较难的sql问题:27(字符串拆分.字符串合并.非连续数字的间隔范围.随机返回字符串) 在论坛中看到一个帖子,帖子中有一些sql方面的面试题,我觉得这些面试题很有代表性. 原帖的 ...

  6. Gogs搭建私有git代码仓库

    前置环境: 数据库 -> mysql git -> 服务端和客户端版本必须>=1.8.3 ssh服务 -> 如果只使用http/https方式的话,服务端无需配置ssh. st ...

  7. iview的table中Tooltip的使用

    这篇文章主要介绍了iview-admin中在table中使用Tooltip提示效果. 1. table中文字溢出隐藏,提示气泡展示所有信息 jLongText(item){ item.render = ...

  8. 【转载】Sqlserver存储过程中使用Select和Set给变量赋值

    Sqlserver存储过程是时常使用到的一个数据库对象,在存储过程中会使用到Declare来定义存储过程变量,定义的存储过程变量可以通过Set或者Select等关键字方法来进行赋值操作,使用Set对存 ...

  9. JS权威指南读书笔记(二)

    第四章 表达式和运算符 1 new调用构造函数的过程     a 创建一个新的空对象     b 设置空对象的_proto_指向构造函数原型prototype     c 将这个新对象当做this的值 ...

  10. vue 分组左右选择

    <el-col :span="12"> <div style="text-align: left" class="transferd ...