Laravel 5
О сервис-провайдерах и репозиториях
Подборка из обсуждений на stackoverflow и laravel.io
1. Комментарий со stackoverflow
Прочитав все доступные книги по Laravel на Leanpub и тонну информации из google, пришел к следующей структуре:
- Одна Eloquent модель для каждой таблицы базы данных
- Один класс репозитория для каждой Eloquent модели
- Класс Service для взаимодействия между классами репозиториев
Предположим, что мы работаем с базой данных видеопродуктов. Тогда у нас будут следующие Eloquent модели:
- Movie
- Studio
- Director
- Actor
- Review
Классы репозиториев для CRUD-операций с базой данных:
- MovieRepository
- StudioRepository
- DirectorRepository
- ActorRepository
- ReviewRepository
Каждый из этих репозиториев будет наследоваться от базового репозитория BaseRepository со следующим интерфейсом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
interface BaseRepositoryInterface { public function errors(); public function all(array $related = null); public function get($id, array $related = null); public function getWhere($column, $value, array $related = null); public function getRecent($limit, array $related = null); public function create(array $data); public function update(array $data); public function delete($id); public function deleteWhere($column, $value); } |
Service класс нам понадобится для работы с несколькими репозиториями и будет содержать бизнес-логику приложения. Контроллеры будут только взаимодействовать с Service классами для Create, Update и Delete действий.
Например, если я хочу создать новую Movie запись в базе, то MovieController класс будет содержать следующие методы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public function __construct(MovieRepositoryInterface $movieRepository, MovieServiceInterface $movieService) { $this->movieRepository = $movieRepository; $this->movieService = $movieService; } public function postCreate() { if( ! $this->movieService->create(Input::all())) { return Redirect::back()->withErrors($this->movieService->errors())->withInput(); } // Новое видео успешно сохранено. Здесь любой ваш код } |
Будем считать, что данные возвращаются из Input::all() в методе postCreate():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$data = array( 'movie' => array( 'title' => 'Iron Eagle', 'year' => '1986', 'synopsis' => 'When Doug\'s father, an Air Force Pilot, is shot down by MiGs belonging to a radical Middle Eastern state, no one seems able to get him out. Doug finds Chappy, an Air Force Colonel who is intrigued by the idea of sending in two fighters piloted by himself and Doug to rescue Doug\'s father after bombing the MiG base.' ), 'actors' => array( 0 => 'Louis Gossett Jr.', 1 => 'Jason Gedrick', 2 => 'Larry B. Scott' ), 'director' => 'Sidney J. Furie', 'studio' => 'TriStar Pictures' ) |
Так как MovieRepository не обязан знать, как создавать записи Actor, Director или Studio в базе, то мы будем использовать MovieService класс:
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 |
public function __construct(MovieRepositoryInterface $movieRepository, ActorRepositoryInterface $actorRepository, DirectorRepositoryInterface $directorRepository, StudioRepositoryInterface $studioRepository) { $this->movieRepository = $movieRepository; $this->actorRepository = $actorRepository; $this->directorRepository = $directorRepository; $this->studioRepository = $studioRepository; } public function create(array $input) { $movieData = $input['movie']; $actorsData = $input['actors']; $directorData = $input['director']; $studioData = $input['studio']; // In a more complete example you would probably want to implement database transactions and perform input validation using the Laravel Validator class here. // Create the new movie record $movie = $this->movieRepository->create($movieData); // Create the new actor records and associate them with the movie record foreach($actors as $actor) { $actorModel = $this->actorRepository->create($actor); $movie->actors()->save($actorModel); } // Create the director record and associate it with the movie record $director = $this->directorRepository->create($directorData); $director->movies()->associate($movie); // Create the studio record and associate it with the movie record $studio = $this->studioRepository->create($studioData); $studio->movies()->associate($movie); // Assume everything worked. In the real world you'll need to implement checks. return true; } |
Таким образом нам удается получить разумное разделение задач. Репозитории знают только о Eloquent моделях, данные которых они добавляют и извлекают в базе. Контроллеры не заботятся о репозиториях, они просто собирают данные пользователя и передают их соответствующему сервису. Сервис не заботится о том, как именно полученные данные сохраняются в базу, он просто передает данные из контроллера в нужные репозитории.