WEB-Программист
Переключить навигацию

Язык

  • Русский
  • Русский
Связаться с нами

Поиск

  • Книги
  • JavaScript
  • HTML и CSS
  • Другие
  • SEO
  • wordpress
  • Дизайн
  • Laravel
  • Phyton
  • React js
  • Android
  • SQL и языки запросов
  • Yii
  • Шрифты
  • Статьи
  • Laravel
  • wordpress
  • Темы Wordpress
  • Интернет магазин
  • JavaScript
  • © 2015-2026 Andrii Beznosko

  • Hosting CityHost

Паттерн Репозиторий и Laravel

  • Описания
  • Описание/Скачать

Репозиторий обычно используется как хранилище данных, часто для обеспечения безопасности или сохранности — Википедия.

Вот как Википедия описывает репозитории. И так сложилось, что в отличии от других различных определений с которыми мы сталкиваемся — это подходит идеально. Репозиторий олицетворяет концепцию хранилища коллекции конкретного типа сущности.

Репозиторий как коллекция

Вероятно самое важное свойство репозиториев это то, что они олицетворяют коллекцию сущностей. Они не являются хранилищем в базе данных или кэше, или тому подобному. Репозитории являются коллекциями. Как вы используете эти коллекции — это просто детали реализации.

Я хочу немного прояснить на этой стадии. Репозиторий это коллекция, коллекция которая содержит сущности, которые могут быть как либо отфильтрованы и возвращены назад в зависимости от требований вашего приложения. Как именно они содержат эти сущности — это ДЕТАЛЬ РЕАЛИЗАЦИИ.

В мире PHP мы используем цикл Запрос/Ответ, сопровождающийся смертью PHP процесса. Всё, что не хранится внешне уничтожается навсегда в этом случае. Сейчас не все платформы работают по этому принципу.

Я нахожу хорошим мысленным экспериментом для того чтобы понять репозитории, это представить что ваше приложение всегда запущено и что объекты всегда остаются в памяти. Мы не беспокоимся о критических проблемах в этом эксперименте. Представьте что у вас есть одиночный репозиторий для сущности Member — MemberRepository.

Затем вы создаёте нового Member и добавляете его в репозиторий. Позже вы запрашиваете у репозитория всех members и получаете назад коллекцию, которая содержит Memberкоторого вы добавили. Возможно вы захотите получить отдельного Member по ID, вы можете сделать это тоже. Легко представить что внутри репозитория эти объекты Memberхранятся как массив или лучше как коллекция объектов.

Проще говоря, репозиторий это специальный тип управляющей коллекции, которую вы используете снова и снова для хранения и получения обратно сущностей.

Взаимодействие с репозиторием

Представьте что вы создали сущность Member. Вы удовлетворены объектом Member, затем когда запрос заканчивается, объект Member исчезает. Затем Member пытается авторизироваться в вашем приложении и не может. Очевидно что мы должны сделатьMember доступным в других частях нашего приложения.

<?php

$member = Member::register($email, $password);
$memberRepository->save($member);

 

Затем мы захотели получить Member позже, например так:

<?php

$member = $memberRepository->findByEmail($email);
// или
$members = $memberRepository->getAll();

 

Теперь мы можем хранить объекты Member в одной части нашего приложения, и затем получать их в другой части.

Должен ли репозиторий создавать сущности?

Вы могли делать что-то вроде этого:

<?php
    
$member = $memberRepository->create($email, $password);

 

Я видел людей которые приводили аргументы для этого подхода. Но я крайне не рекомендую его.

Ещё раз, репозитории это коллекции. Я не уверен что коллекция должна быть ещё и фабрикой. Я слышал аргументы типа.. они хранят состояние, почему же не хранить ещё и создание сущностей?

В моём разуме это анти-паттерн. Почему не разрешить Member иметь свои собственные представления о своём создании, или почему не иметь фабрику которая специально разработана для обеспечения создания более комплексных объектов.

Если мы используем наши репозитории как простые коллекции, тогда мы даём им одну ответственность. Я не хочу классы коллекции которые так же являются фабриками.

В чём преимущество репозиториев?

Главное преимущество репозиториев — это абстрактный механизм хранилища для управляющей коллекции сущностей.

Когда мы создаём интерфейс MemberRepository, мы разрешаем существование любого числа его конкретных реализаций:

<?php

interface MemberRepository {
    public function save(Member $member);
    public function getAll();
    public function findById(MemberId $memberId);
}

 

Первая реализация:

<?php
    
class ArrayMemberRepository implements MemberRepository {
    private $members = [];

    public function save(Member $member) {
        $this->members[(string)$member->getId()] = $member;
    }

    public function getAll() {
        return $this->members;
    }

    public function findById(MemberId $memberId) {
        if (isset($this->members[(string)$memberId])) {
            return $this->members[(string)$memberId];
        }
    }
}

 

И ещё одна:

<?php

class RedisMemberRepository implements MemberRepository {
    public function save(Member $member) {
        // ...
    }

    // you get the point
}

 

Если идти по этому пути, то наше приложения знает только абстрактную концепциюMemberRepository и наше использование этого репозитория может быть разделено на несколько реализаций. Это вполне гибко.

Репозитории принадлежат к слою домена или слою приложения?

Это достаточно интересный вопрос. Во-первых, давайте определим слой приложения как многослойную архитектуру, которая ответственна за реализацию конкретных деталей работы приложения, таких как работа с БД, знания о протоколе передачи данных(отправка email, взаимодейcтвие с API) и др.

Давайте определим слой домена как многослойную архитектуру, которая ответственна за хранение бизнес-правил и бизнес-логики.

Работая с этими определениями, в которое из них подходит наш репозиторий?

Давайте посмотрим на наш пример из кода выше:

<?php

class ArrayMemberRepository implements MemberRepository {
    private $members = [];

    public function save(Member $member) {
        $this->members[(string) $member->getId()] = $member;
    }

    public function getAll() {
        return $this->members;
    }

    public function findById(MemberId $memberId) {
        if (isset($this->members[(string)$memberId])) {
            return $this->members[(string)$memberId];
        }
    }
}

 

В этом примере я вижу множество деталей реализации. Эти детали реализации безусловно относятся к слою приложения.

Давайте уберём все детали реализации из этого класса…

<?php
    
class ArrayMemberRepository implements MemberRepository {
    public function save(Member $member) {
    }

    public function getAll() {
    }

    public function findById(MemberId $memberId) {
    }
}

 

Хм, это кажется достаточно знакомым. Где же это было?

Может быть это напоминает вам это?

<?php

interface MemberRepository {
    public function save(Member $member);
    public function getAll();
    public function findById(MemberId $memberId);
}

 

Это значит что интерфейс находится на границе слоёв. Сам интерфейс может содержать специфичные для домена концепции, но реализация интерфейса не должна.

Интерфейс репозитория принадлежит к слою домена. Реализация интерфейса принадлежит к слою приложения. Это значит что мы можем писать подсказки к нашим репозиториям в слое домена даже не дотрагиваясь к слою приложения.

Свобода выбора хранилища данных

Вы наверное слышали в разговорах о концепциях объектно-ориентированного программирования что-то типа такого — «и вы полностью неограничены в смене одного хранилища данных на другое позже».

Я пришёл к выводу что это не совсем правда.. это очень слабый аргумент. Самая большая проблема с этим объяснением, это то, что оно приводит к вопросу — «А вы действительно захотите менять хранилище данных?». Я не хочу чтобы ответ на этот вопрос определял использовать или нет паттерн репозиторий.

Любое хорошо спроектированное объектно-ориентированное приложения автоматически идёт с этим типом преимущества. Центральная концепция объектной-ориентированности это инкапсуляция. Вы можете показать API и скрыть реализацию.

Правда в том, что вы наверное не захотите менять одну ORM на другую. Но у вас хотя бы будет хороший способ реализовать это. Тем не менее, смена реализаций репозиториев отлично подходит для тестирования.

Тестируемость с паттерном репозиторий

Угадайте что? Это очень вкусно. Допустим у вас есть объект, который содержит что-либо похожее на регистрацию участников:

<?php

class RegisterMemberHandler {
    private $members;

    public function __construct(MemberRepository $members) {
        $this->members = $members;
    }

    public function handle(RegisterMember $command) {
        $member = Member::register($command->email, $command->password);
        $this->members->save($member);
    }
}

 

В ходе обычных операций вы можете сделать инъекцию реализации MemberRepository —DoctrineMemberRepository. Тем не менее в ходе тестирования вы можете заменить её наArrayMemberRepository. Обе они реализуют интерфейс

Упрощённая версия теста может быть такой..

 <?php
 
 $repo = new ArrayMemberRepository;
 $handler = new RegisterMemberHandler($repo);
 
 $request = $this->createRequest(['email' => '[email protected]', 'password' => 'angelofdestruction']);
 
 $handler->handle(RegisterMember::usingForm($request));
 
 AssertCount(1, $repo->findByEmail('[email protected]'));

 

В этом примере мы тестируем обработчик. Нам не нужно тестировать то что репозиторий хранит данные в базе или где-то ещё. Нам нужно протестировать поведение этого объекта, который должен запросить у класса Member нового участника на основе аргументов команды, затем положить его в репозиторий.

Коллекцие-Ориентированный или Состояние-Ориентированный

В книге Реализация DDD, Vaughn Vernon делает различие между основанными на состоянии и на коллекциях репозиториями. Вкратце, идея репозиториев основанных на коллекциях это то, что с данными обращаются как с хранилищем массива в памяти. Но в ориентированном на состоянии репозитории всё сводится к тому что данных хранятся глубже. Это исходит из их названий.

У меня сейчас нет мнения по поводу того, который использовать. Тем не менее, я осторожен с этим. Сейчас я фокусируюсь на репозиториях как на коллекциях объектов с такой же ответственностью какую и любая другая коллекция объектов может иметь.

Итог

Я верю в то что…

.. важно дать репозиториям единственное задание — функционировать как коллекция объектов

.. мы не должны использовать репозитории для создания сущностей

.. мы должны легко уметь менять технологию с помощью которой мы получаем данные, так как это приносить массу преимуществ, которые сложно недооценить

В будущем я хочу написать ещё несколько статей о репозиториях, например о том как кешировать результаты репозитория используя паттерн декоратор, проведение запросов с помощью паттерна критерия, роль репозитория в сборке в одно целое вариантов реализации, и реализация группы операций с большим числом объектов.

Комментарии
Всего комментариев: 0
Оставить комментарий Отменить ответ

Ваш email не будет опубликован.

Жанр: Главная » Статьи » Laravel » Паттерн Репозиторий и Laravel
Статус: Для продвинутых программистов
Ссылка на оригинал статьи (Если указана или эта статья не авторская) Скачать
На сайт предоставил Сен 18, 2015 14:20 Andriy

Статьи опубликованные на сайте WEB-Программист указаны со ссылками на источник. Администрация сайта не несет ответственность за их использование Вами

Laravel
Previous Next

Смотри также:

Паттерн Репозиторий и Laravel

Репозиторий обычно используется как хранилище данных, часто для обеспечения безопасности или сохранности — Википедия. Вот как Википедия описывает репозитории. И так сложилось, что в отличии от других различных определений с которыми мы сталкиваемся — это подходит идеально. Репозиторий олицетворяет концепцию...

Интеграционное тестирование в Laravel 5.1: Фабрики моделей

При тестировании кода часто приходится создавать тестовые записи одной или нескольких моделей. Вы могли использовать нечто такое: $post = new Post; $post->title = 'Fake Blog Post Title'; $post->body = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam lorem erat,...

Расширяем классы Laravel методом `orAbort` при помощи трейта

Вдохновлённый статьёй Edd Man's об опциональных управляющих потоках, я создал небольшой пакет Laravel для реализации опциональной остановки приложения. Он предоставляет трейт Spatie\OrAbort\OrAbort, который может использоваться с любым классом. Всем методам класса добавляется orAbort функциональность. Когда оригинальный метод возвращает false, будет...

Изменяем стандартную страницу ошибки в Laravel 5.1

Всем нам знакома страница Whoops, looks like something went wrong. Мы можем легко модифицировать её под нужды нашего клиентского шаблона. Для этого достаточно изменить файл app/Exceptions/Handler.php, переопределив в классе Handler методconvertExceptionToResponse: <?php namespace App\Exceptions; use Exception; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;...

Трюки Eloquent для лучших репозиториев (Laravel)

Одна из лучших вещей в написании кода - очевидность хороших практик, ведь если им не следовать, возникает раздражение. Очень надоедает, когда вам нужно писать одну и ту же вещь снова и снова. Когда вы чувствуете себя недовольным из-за повторения одних...

Связаться с нами

- Премиум темы и плагины WP Star бесплатно -

We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept”, you consent to the use of ALL the cookies.
Cookie settingsACCEPT
Manage consent

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
CookieDurationDescription
cookielawinfo-checbox-analytics11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checbox-functional11 monthsThe cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checbox-others11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-necessary11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-performance11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy11 monthsThe cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytics
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Advertisement
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.
Others
Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.
SAVE & ACCEPT