Две недели назад я обсуждал статью «Два шаблона проектирования, которые сделают ваши приложения лучше». После этого появился огромный интерес к демонстрации шаблона «Репозиторий» в действии. Сегодня мы посмотрим, как репозиторий подходит для Laravel Faq Page.
Просто говоря, репозиторий — это абстрактный слой между каким-либо хранилищем и вашим приложением или бизнес-логикой. Это примерно то же, как если бы вы могли подойти к полке для книг и взять нужные из них одним движением руки. Итак, давайте посмотрим на задачи, которые мы хотим решить взаимодействием между хранилищем данных или слоем постоянного хранилища.
- Выборка всех заданных вопросов
- Создание пустого вопроса для связывания формы в представлении
- Страничный вывод запросов
- Создание и хранение вопроса
- Выборка вопросов по первичному ключу
- Обновление вопроса с первичным ключом и некоторым массивом данных
- Удаление вопроса по первичному ключу
Если вы уже знакомы с Laravel, то у вас, вероятно, появится вопрос: «что не так с Eloquent?». Единственное не совсем стандартное его использование снаружи от модели — обновление вопроса по первичному ключу и атрибутам.
Однако я скрыл несколько требований. Первое — наши вопросы используют пакет Eloquent Rankable и наши коллекции выборок должны быть отсортированы по важности. Второе — страничный вывод должен быть только для отвеченных вопросов. Таким образом, мы думаем о репозитории как о методе исключения специфической логики базы данных из наших контроллеров. (Что, несомненно, делает Тейлора очень счастливым.)
Для начала мы создадим интерфейс для именования наших методов и их аргументов. Этот интерфейс позже будет реализован в нашем хранилище, использующем Eloquent. В src/Rtablada/LaravelFaq/Repositories/FaqRepository.php создадим следующее.
<?php namespace Rtablada\LaravelFaq\Repositories;
interface FaqRepository
{
public function all($columns = array('*'));
public function newInstance(array $attributes = array());
public function paginate($perPage = 15, $columns = array('*'));
public function create(array $attributes);
public function find($id, $columns = array('*'));
public function updateWithIdAndInput($id, array $input);
public function destroy($id);
}
Имена функций были выбраны по двум причинам. Первое, имена методов четко показывают действия, которые они совершают, и аргументы, которые могут им понадобиться. Кроме того, мы уже знакомы с API Eloquent, поэтому реализация будет понятна и проста.
Теперь давайте реализуем наш репозиторий в %%(t)src/Rtablada/LaravelFaq/Repostiories/FaqRepositoryEloquent.php:
<?php namespace Rtablada\LaravelFaq\Repositories;
use Rtablada\LaravelFaq\Faq;
use Config;
class FaqRepositoryEloquent implements FaqRepository
{
protected $faqModel;
public function __construct(Faq $faqModel)
{
$this->faqModel = $faqModel;
}
public function newInstance(array $attributes = array())
{
if (!isset($attributes['rank'])) {
$attributes['rank'] = 0;
}
return $this->faqModel->newInstance($attributes);
}
public function paginate($perPage = 0, $columns = array('*'))
{
$perPage = $perPage ?: Config::get('laravel-faq::pagination.length');
return $this->faqModel->rankedWhere('answered', 1)->paginate($perPage, $columns);
}
public function all($columns = array('*'))
{
return $this->faqModel->rankedAll($columns);
}
public function create(array $attributes)
{
return $this->faqModel->create($attributes);
}
public function find($id, $columns = array('*'))
{
return $this->faqModel->findOrFail($id, $columns);
}
public function updateWithIdAndInput($id, array $input)
{
$faq = $this->faqModel->find($id);
return $faq->update($input);
}
public function destroy($id)
{
return $this->faqModel->destroy($id);
}
}
Как вы видите, мы использовали синтаксис ranked* из Eloquent Rankable в некоторых наших методах. Этот синтаксис в нашем контроллере ограничит свободу тому, кто хочет создать «плоскую» файловую версию пакета. Это так же убирает логику из нашего контроллера, что и является основной целью нашей абстракции. Теперь, если мы захотим что-то изменить в логике получения записей из БД, нам не потребуется искать обращения к ней во всех наших контроллерах и изменять их.
Наконец, в src/Rtablada/LaravelFaq/LaravelFaqServiceProvider.php мы должны добавить связывания и сказать Laravel, что всякий раз, когда мы запрашиваем экземпляр PHPFaqRepository
, мы на самом деле хотим использовать реализацию PHPFaqRepositoryEloquent
. Мне нравится выносить связывания в отдельные методы — это позволяет впоследствии изменять логику, которая не загромождает функцию загрузки PHPboot
. В boot добавим следующее:
$this->bootRepositories();
И далее создадим функцию PHPbootRepositories
:
public function bootRepositories()
{
$this->app->bind('Rtablada\LaravelFaq\Repositories\FaqRepository',
'Rtablada\LaravelFaq\Repositories\FaqRepositoryEloquent');
}
И это все. Теперь у вас есть быстрая абстракция, позволяющая изменять логику и даже производить изменения всей структуры.
Комментарии (2)
Паттер Репозиторий не используется для добавления или обновления записей. Для этого существует Паттерн «Сервисный Слой».
Это не совсем так, в Репозитории осуществляется хранение логики моделей (а уже в модели определяется, будет ли это бд, файл, и т.д.). К примеру, передаем данные модели и указываем, что нужно сохранить пользователя и его профиль, а модель сохранит куда нужно.
В Сервисном Слое мы реализуем бизнес-логику хранения. К примеру, передаем данные репозиторию пользователя и говорим, что хотим сохранить его. А еще передаем другому репозиторию данные отзыва пользователя и также говорим сохранить его. И т.д.