Russian (Pусский) translation by Sergey Zhuk (you can also view the original English article)
Паттерн репозиторий впервые был представлен в книге Эрика Еванса Предметно-Ориентированное Проектирование. В действительности в приложении репозиторий является входной точкой для получения доступа к данным предметной области.
Проще говоря, репозиторий позволяет всей кодовой базе использовать объекты, и при этом не вдаваться в детали, как эти объекты хранятся. Репозиторий сам содержит всю необходимую информацию о том, каким образом хранятся объекты, включая преобразование таблиц базы в объекты. Это предоставляет более объектно-ориентированный подход к уровню хранения данных в приложении и делает код, который занимается преобразованиями объектов более инкапсулированным.
Единственный способ заставить ваши репозитории работать в проекте Laravel (именно так как они описаны Эриком Евансом в его книге) - изменить используемую ORM по умолчанию с active record на data mapper. Лучшей заменой является Doctrine.
Doctrine ORM
Doctrine является ORM (объектно-реляционноым отображением), которое реализует паттерн data mapper, что позволяет сделать чистым разделение между бизнес логикой приложения и уровнем хранения данных в базе. Doctrine использует DQL вместо SQL. DQL предоставляет язык объектных запросов, имея в виду что вместо традиционных реляционных выражений, будут использоваться запросы с объектными выражениями.
Это позволяет составлять запросы к базе данных в объектно-ориентированном стиле и помогает, когда нужно выполнить запрос к базе таким способом, который недоступен простыми методами репозитория. По моему мнению DQL является наиболее мощным способом поддерживать связь с базой данных.
Doctrine vs. Eloquent
Сущности Doctrine являются обыкновенными классами PHP и не добавляют дополнительных расходов на наследование ORM. Doctrine управляет несколькими запросами в одной иерархии без обращения к базе данных, то есть объект сущности существует для всего запроса.
Другой интересной возможностью Doctrine является то, что вместо написания миграций для создания схемы базы, база автоматически создается так, чтобы отражать мета данные, описанные в аннотациях сущностей. С другой стороны Eloquent является менее сложной и очень легкой в использовании.
Подробное сравнение этих двух ORM потребовало бы отдельной статьи. Как вы видите, объект Doctrine является более абстрактным. Однако Doctrine будет удовлетворять потребности только специфичных проектов, что может приводить иногда к некоторым накладным расходам. Я считаю, что выбор подходящей ORM для приложения полностью зависит от предпочтений программиста.
Приложение Блог
Пришло время создать простое приложение с блогом на Laravel. Сперва нам следует настроить Doctrine. Существует специальная версия ORM для использования в проектах Laravel 5. Чтобы установить Doctrine 2 в наш проект Laravel, нужно выполнить следующую команду:
composer require laravel-doctrine/orm
Как обычно пакет следует добавить в app/config.php
с помощью service provier'a
LaravelDoctrine\ORM\DoctrineServiceProvider::class,
Так же следует настроить псевдоним:
'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager::class
В заключении мы публикуем настройки пакета:
php artisan vendor:publish --tag="config"
Теперь все готово.
Сущности являются важной частью приложения App\Entities\Post.php
:
namespace App\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="posts") * @ORM\HasLifecycleCallbacks() */ class Post { /** * @var integer $id * @ORM\Column(name="id", type="integer", unique=true, nullable=false) * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") * */ private $id; /** * @ORM\Column(type="string") */ private $title; /** * @ORM\Column(type="text") */ private $body; public function __construct($input) { $this->setTitle($input['title']); $this->setBody($input['body']); } public function setId($id) { return $this->id=$id; } public function getId() { return $this->id; } public function getTitle() { return $this->title; } public function setTitle($title) { $this->title = $title; } public function getBody() { return $this->body; } public function setBody($body) { $this->body = $body; } }
Теперь пришло время создать Репозиторий, который был описан ранее. App/Repositories/PostRepo.php
:
namespace App\Repository; use App\Entity\Post; use Doctrine\ORM\EntityManager; class PostRepo { /** * @var string */ private $class = 'App\Entity\Post'; /** * @var EntityManager */ private $em; public function __construct(EntityManager $em) { $this->em = $em; } public function create(Post $post) { $this->em->persist($post); $this->em->flush(); } public function update(Post $post, $data) { $post->setTitle($data['title']); $post->setBody($data['body']); $this->em->persist($post); $this->em->flush(); } public function PostOfId($id) { return $this->em->getRepository($this->class)->findOneBy([ 'id' => $id ]); } public function delete(Post $post) { $this->em->remove($post); $this->em->flush(); } /** * create Post * @return Post */ private function perpareData($data) { return new Post($data); } }
Контроллер:App/Http/Controllers/PostController.php
:
namespace App\Http\Controllers; use App\Repository\PostRepo as repo; use App\Validation\PostValidator; class PostController extends Controller { private $repo; public function __construct(repo $repo) { $this->repo = $repo; } public function edit($id=NULL) { return View('admin.edit')->with(['data' => $this->repo->postOfId($id)]); } public function editPost() { $all = Input::all(); $validate = PostValidator::validate($all); if (!$validate->passes()) { return redirect()->back()->withInput()->withErrors($validate); } $Id = $this->repo->postOfId($all['id']); if (!is_null($Id)) { $this->repo->update($Id, $all); Session::flash('msg', 'edit success'); } else { $this->repo->create($this->repo->perpare_data($all)); Session::flash('msg', 'add success'); } return redirect()->back(); } public function retrieve() { return View('admin.index')->with(['Data' => $this->repo->retrieve()]); } public function delete() { $id = Input::get('id'); $data = $this->repo->postOfId($id); if (!is_null($data)) { $this->repo->delete($data); Session::flash('msg', 'operation Success'); return redirect()->back(); } else { return redirect()->back()->withErrors('operationFails'); } } }
Как видно я использовал пакет Flash для управления сообщениями (можно использовать пакет поставляемый с Laravel). Что касается валидатора, я хотел бы добавить, что вы можете создать свой собственный (так как это сделал я) или использовать предоставляемый в Laravel по умолчанию, все зависит от ваших предпочтений.
Файлы отображения такие же как и обычно. В этом примере отображения файл будет таким наresources/views/admin/edit.blade.php
:
<div class="panel-body"> <div class="row" > <div class="col-md-12"> @if (Session::has('flash_notification.message')) <div class="alert alert-{{ Session::get('flash_notification.level') }}"> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> {!! Session::get('flash_notification.message') !!} </div> @endif </div> </div> @if($errors->has()) <div class="alert alert-danger" role="danger" > @foreach ($errors->all() as $error) <ul> <li>{!! $error !!}</li> </ul> @endforeach </div> @endif <form method="post" > <div class="row"> <div class="col-md-6"> <div class="input-group {!! $errors->first('title')?'has-error':'' !!}"> <div class="input-group-addon">{!! 'title' !!}</div> <input type="text" name="title" value="{!! is_object($ListData)?$ListData->getBody():'' !!}" class ='form-control' autocomplete= 'off' /> </div> <span class="help-block"> </span> </div> <div class="col-md-6"> <div class="input-group {!! $errors->first('body')?'has-error':'' !!}"> <div class="input-group-addon">{!! 'Body' !!}</div> <textarea name="body" class ='form-control' autocomplete= 'off' > {!! is_object($ListData)?$ListData->getTitle():'' !!} </textarea> </div> <span class="help-block"> </span> </div> </div> <div class="row"> <br/> <div class="col-md-6"> <div class="col-sm-5 submitWrap"> <button type="submit" class="btn btn-primary btn-md" > {!! 'save' !!}</button> </div> </div> </div> </form> </div> </div> </div>
Маршрутизация и другие операции останутся без изменений.
Заключение
Теперь вы видите как можно легко создать репозиторий на основе Doctrine в проекте Laravel 5.0, что будет иметь множество преимуществ.
Для тех из вас кто только начинает изучать Laravel или стремиться расширить свои знания, сайт или приложения с расширениями, мы предоставляем множество вещей для изучения в Envato Market.
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post