search
HomePHP FrameworkLaravelA brief analysis of the advantages of the repository pattern (Repository) in Laravel

Why use the repository pattern (Repository) in Laravel? The following article will introduce to you the advantages of using the repository mode. I hope it will be helpful to you!

A brief analysis of the advantages of the repository pattern (Repository) in Laravel

In a previous article, I explained what the repository pattern is, how it differs from the Active Record pattern, and how to implement it in Laravel. Now I want to take a closer look at why you should use the repository pattern.

I noticed in the comments of the previous article that the Repository pattern is a controversial topic in the Laravel community. Some people see no reason to use it and stick with the built-in Active Record mode. Others prefer to use other methods to separate data access from the logical domain. Please note that I respect these opinions and will devote an upcoming blog post to this topic.

With this disclaimer, let’s understand the advantages of using the repository pattern.

Single Responsibility Principle

The Single Responsibility Principle is the main discriminator to differentiate between Active Record mode and Repository mode. Model classes already hold data and provide methods on domain objects. When using the Active Record pattern, data access is an additional responsibility introduced. This is something I want to illustrate in the following example:

/**
 * @property string $first_name
 * @property int    $company_id
 */
class Employee extends Model {}

$jack = new Employee();
$jack->first_name = 'Jack';
$jack->company_id = $twitterId;
$jack->save();

Although the domain model and data access technology have mixed responsibilities, it intuitively makes sense. In our application the employees have to be stored in the database somehow, so why not call save() on the object. A single object is converted into a single row of data and stored.

But let's go a step further and see what else we can do with employees:

$jack->where('first_name', 'John')->firstOrFail()->delete();
$competition = $jack->where('company_id', $facebookId)->get();

Now, it becomes unintuitive and even violates our domain model. Why would Jack suddenly delete another employee who might even be working at a different company? Or why he was able to bring Facebook employees over?

Of course, this example is contrived, but it still shows how the Active Record pattern does not allow for intentional domain models. The line between employees and the list of all employees becomes blurred. You always have to consider whether the employee is being used as an actual employee or as a mechanism to access other employees.

Warehouse mode solves this problem by enforcing this basic partitioning. Its only purpose is to identify a collection of domain objects, not the domain object itself.

Key points:

  • #The warehouse pattern embodies the single responsibility principle by separating a collection of all domain objects from a single domain object.
Some projects sprinkle database queries all over the project. Below is an example where we get a list from the database and display them in a Blade view.

class InvoiceController {

    public function index(): View {
        return view('invoices.index', [
            'invoices' => Invoice::where('overdue_since', '>=', Carbon::now())
                ->orderBy('overdue_since')
                ->paginate()
        ]);
    }
}

When a query like this becomes more complex and used in multiple places, consider extracting it into a Repository method.

The repository pattern helps reduce duplicate queries by packaging them into expression methods. If you have to adjust the query, you only need to change it once.

class InvoiceController {

    public __construct(private InvoiceRepository $repo) {}

    public function index(): View {
        return view('invoices.index', [
            'invoices' => $repo->paginateOverdueInvoices()
        ]);
    }
}

Now the query is implemented only once and can be tested in isolation and used elsewhere. Additionally, the Single Responsibility Principle comes into play again, as the controller is not responsible for getting the data, but only for processing the HTTP request and returning the response.

Takeaway:

    ? Repository pattern helps reduce duplicate queries

Dependency Inversion

Explanation

Dependency Inversion Principle It deserves its own blog post. I just wanted to illustrate that a repository can enable dependency inversion.

When layering components, typically higher-level components depend on lower-level components. For example, the controller will rely on the model class to get data from the database:

class InvoiceController {
    public function index(int $companyId): View {
        return view(
            'invoices.index',
            ['invoices' => Invoice::where('company_id', $companyId)->get()]
        );
    }
}

The dependency is top-down and tightly coupled.

InvoiceController depends on the specific Invoice class. It is difficult to decouple these two classes, such as testing them separately or replacing the storage mechanism. By introducing the Repository interface, we can achieve dependency inversion:

interface InvoiceRepository {
    public function findByCompanyId($companyId): Collection;
}

class InvoiceController {
    public function __construct(private InvoiceRepository $repo) {}

    public function index(int $companyId): View {
        return view(
            'invoices.index',
            ['invoices' => $this->repo->findByCompanyId($companyId)]
        );
    }
}

class EloquentInvoiceRepository implements InvoiceRepository {
    public function findByCompanyId($companyId): Collection {
        // 使用 Eloquent 查询构造器实现该方法
    }
}

Controller now only depends on the Repository interface, the same as the Repository implementation.

These two classes now only depend on one abstraction, thus reducing Coupling. As I will explain in the next section, this brings further advantages.

Takeaway:

  • ? 存储库模式作为一种抽象类,支持依赖反转.

存储库 提高了可读性 因为复杂的操作被具有表达性名称的高级方法隐藏了.

访问存储库的代码与底层数据访问技术分离. 如有必要,您可以切换实现,甚至可以省略实现,仅提供 Repository 接口。 这对于旨在与框架无关的库来说非常方便。

OAuth2 服务包 ——  league/oauth2-server 也用到这个抽象类机制。 Laravel Passport 也通过 实现这个库的接口 集成 league/oauth2-server 包。

正如 @bdelespierre评论 里回应我之前的一篇博客文章时向我指出的那样,你不仅可以切换存储库实现,还可以将它们组合在一起。大致以他的示例为基础,您可以看到一个存储库如何包装另一个存储库以提供附加功能:

interface InvoiceRepository {
    public function findById(int $id): Invoice;
}

class InvoiceCacheRepository implements InvoiceRepository {

    public function __construct(
        private InvoiceRepository $repo,
        private int $ttlSeconds
    ) {}

    public function findById(int $id): Invoice {
        return Cache::remember(
            "invoice.$id",
            $this->ttlSeconds,
            fn(): Invoice => $this->repo->findById($id)
        );
    }
}

class EloquentInvoiceRepository implements InvoiceRepository {

    public function findById(int $id): Invoice { /* 从数据库中取出 $id */ }
}

// --- 用法:

$repo = new InvoiceCacheRepository(
    new EloquentInvoiceRepository();
);

要点:

  • ? 存储库模式抽象了有关数据访问的详细信息。
  • ? 存储库将客户端与数据访问技术分离
  • ? 这允许切换实现,提高可读性并实现可组合性。

可测试性

存储库模式提供的抽象也有助于测试。

如果你有一个 Repository 接口,你可以提供一个替代的测试实现。 您可以使用数组支持存储库,而不是访问数据库,将所有对象保存在数组中:

class InMemoryInvoiceRepository implements InvoiceRepositoryInterface {

    private array $invoices;

    // implement the methods by accessing $this->invoices...
}

// --- Test Case:

$repo = new InMemoryInvoiceRepository();
$service = new InvoiceService($repo);

通过这种方法,您将获得一个现实的实现,它速度很快并且在内存中运行。 但是您必须为测试提供正确的 Repository 实现,这 ** 本身可能需要大量工作**。 在我看来,这在两种情况下是合理的:

  • 您正在开发一个(与框架无关的)库,它本身不提供存储库实现。

  • 测试用例复杂,Repository 的状态很重要。

另一种方法是“模仿”,要使用这种技术,你不需要适当的接口。你可以模仿任何 non-final 类。
使用 PHPUnit API ,您可以明确规定如何调用存储库以及应该返回什么。

$companyId = 42;

/** @var InvoiceRepository&MockObject */
$repo = $this->createMock(InvoiceRepository::class);

$repo->expects($this->once())
    ->method('findInvoicedToCompany')
    ->with($companyId)
    ->willReturn(collect([ /* invoices to return in the test case */ ]));

$service = new InvoiceService($repo);

$result = $service->calculateAvgInvoiceAmount($companyId);

$this->assertEquals(1337.42, $result);

有了 mock,测试用例就是一个适当的单元测试。上面示例中测试的唯一代码是服务。没有数据库访问,这使得测试用例的设置和运行非常快速

另外:

  • ? 仓库模式允许进行适当的单元测试,这些单元测试运行快并且是隔离的

原文地址:https://dev.to/davidrjenni/why-use-the-repository-pattern-in-laravel-2j1m

译文地址:https://learnku.com/laravel/t/62521

【相关推荐:laravel视频教程

The above is the detailed content of A brief analysis of the advantages of the repository pattern (Repository) in Laravel. For more information, please follow other related articles on the PHP Chinese website!

Statement
This article is reproduced at:learnku. If there is any infringement, please contact admin@php.cn delete
php怎么把负数转为正整数php怎么把负数转为正整数Apr 19, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace(" ","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php怎么判断有没有小数点php怎么判断有没有小数点Apr 20, 2022 pm 08:12 PM

php判断有没有小数点的方法:1、使用“strpos(数字字符串,'.')”语法,如果返回小数点在字符串中第一次出现的位置,则有小数点;2、使用“strrpos(数字字符串,'.')”语句,如果返回小数点在字符串中最后一次出现的位置,则有。

php怎么读取字符串后几个字符php怎么读取字符串后几个字符Apr 22, 2022 pm 08:31 PM

在php中,可以使用substr()函数来读取字符串后几个字符,只需要将该函数的第二个参数设置为负值,第三个参数省略即可;语法为“substr(字符串,-n)”,表示读取从字符串结尾处向前数第n个字符开始,直到字符串结尾的全部字符。

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
2 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
Repo: How To Revive Teammates
1 months agoBy尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: How To Get Giant Seeds
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.

SublimeText3 Linux new version

SublimeText3 Linux new version

SublimeText3 Linux latest version

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)