The repository pattern was introduced for the first time by Eric Evans in his Domain-Driven Design book. The repository is, in fact, the entry point for the application to access the domain layer.
To put it simply, the repository allows all your code to use objects without having to know how the objects are persisted. The repository contains all the knowledge of persistence, including mapping from tables to objects. This provides a more object-oriented view of the persistence layer and makes the mapping code more encapsulated.
The only way to make your repositories work in Laravel (as a real repository—Eric Evans Domain-Driven Design book) is to change the default ORM from active record to data mapper. The best substitute is Doctrine.
The Doctrine ORM
Doctrine is an ORM (object-relational mapping) which implements the data mapper pattern and allows you to make a clean separation of the application’s business rules from the persistence layer of the database. Doctrine uses DQL, rather than SQL. DQL brings you object query language, meaning that instead of a traditional relational query term, you would have queries in object term.
It allows you to write the database queries in an object-oriented way and helps when you need to query the database in a way which can’t be achieved using the default repository methods. In my opinion, DQL is the most powerful way to keep in touch with your database.
Doctrine vs. Eloquent
Doctrine entities are just a plain PHP simple class and do not add overhead to any ORM inheritance. Doctrine manages your multiple query requests with the same inheritance without hitting the database, meaning the entity object exists for the entire request.
The other nice feature of Doctrine is that instead of migrating files to create the database schema, the database is automatically created to reflect the meta data in the entity annotations. On the other hand, Eloquent is less complicated and very easy to use.
A complete comparison between these two would necessitate a separate article. As you can see, a Doctrine object is lighter and more abstract. However, Doctrine will only fit specific projects, so it may bring you overhead at times. I believe it depends on the programmer to choose the best ORM for the app.
The Blog App
Now it’s time to create a blog app with Laravel. First, we need to set up Doctrine. There is a bridge to allow for matching with Laravel 5’s existing configuration. To install Doctrine 2 within our Laravel project, we run the following command:
composer require laravel-doctrine/orm
As usual, the package should be added to the app/config.php
, as the service provider:
LaravelDoctrine\ORM\DoctrineServiceProvider::class,
The alias should also be configured:
'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager::class
Finally, we publish the package configuration with:
php artisan vendor:publish --tag="config"
Now we’re done here.
Entities are important parts of the app 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; } }
Now it’s time to create the Repository, which was described earlier. 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); } }
The controller: 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'); } } }
As you see, I’ve used the Flash helper to manage the messages (you can use Laravel’s). Regarding the Validator, I should add that you can create your own (as I do) or use the Laravel default, depending on your preference.
View files are the same as usual. In this sample view, the file seems like 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>
The routing and other operations would be as usual.
Conclusion
Now you see how you can easily create a repository based on Doctrine in Laravel 5.0, which will result in many benefits.
For those of you who are either just getting started with Laravel or looking to expand your knowledge, site, or application with extensions, we have a variety of things you can study in Envato Market.
Comments