As a PHP developer, you might have come across the term ORM. ORM is a way to work with databases in the same way you work with classes and objects. If you were to delve deeper into how web applications are designed and built, after doing some exploring in their ORM you would find two well-known patterns: Active Record and Data Mapper.
Active Record refers to mapping an object to a database row. Indeed, each row in the database is tied to an object. When you retrieve a row from the database you can update, delete or save using the object itself. That’s how Eloquent and Paris work, and how it’s done in Ruby on Rails.
On the other hand, Data Mapper
is a layer of software which separates the in-memory objects from the database. With Data Mapper the in-memory objects needn’t know that there is even a database present. They need no SQL interface code or knowledge of the database schema. One such solution is Doctrine.
What Is Doctrine?
Doctrine is an ORM 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.
Some of the advantages I discovered while using Doctrine with Laravel are:
- Faster and easier to use.
- Entities are just plain PHP objects.
- Doctrine utilizes a “code first” approach, so you can create entities first, and then generate a database for them automatically. The reverse case is also possible, but I do not recommend it.
- Supports annotations, XML and YAML for schema.
- DQL (a replacement for SQL) abstracts your tables away.
- Doctrine events allow you to easily hook onto specific database events and perform certain actions.
- Repositories are more faithful to the repository pattern.
-
Transactional write-behind
methodology lets Doctrine have less interaction with the Database until theflush()
method is called.
Of course, Doctrine has disadvantages too, but it is up to the programmer to choose the right ORM.
Doctrine DQL
DQL stands for Doctrine Query Language. DQL brings you object query language, which means that instead of a traditional relational query, you have queries in object form.
DQL allows you to write database queries in an object-oriented way, which is helpful when you need to query the database in a way which cannot be achieved (or is very difficult) using the default repository methods.
Sample DQL Query:
SELECT b.id as ItemId, b.title as ItemTitle , b.url as ItemUrl FROM Alireza\Domain\Identity\Entities\Menu u WHERE u.id =:id
Doctrine Filters
Doctrine allows you to limit query results with Filters. For example, you may want to edit only the information of the logged-in user or make sure the current client’s data was returned from the database. A filter is an automatic solution for remembering specific conditions for all your queries.
Doctrine provides SQL level limitations, so there is no need to maintain the clause in multiple repositories of your project. This enhances security and makes your code easier to read.
Let’s look at an example:
/** * @ManyToOne(targetEntity="User") * @JoinColumn(name="user_id", referencedColumnName="id") **/ private $user;
As you can see in the User entity, the result of JoinColumn
is limited to only items with the condition of WHERE user_id = :user_id
.
Setting Up Doctrine 2
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"
Doctrine needs no database configuration and uses the current Laravel configuration, but if you want to override it you should change the Doctrine config file in Config/doctrine.php
:
'managers' => [ 'default' => [ 'dev' => env('APP_DEBUG'), 'meta' => env('DOCTRINE_METADATA', 'annotations'), 'connection' => env('DB_CONNECTION', 'mysql'), 'namespaces' => [ 'App' ],
That’s all there is to it.
What Is an Entity?
“Entity” refers to an object which has a distinct identity. An entity must have a specific identifier which is unique throughout the entire system, such as a customer or a student. There would be other objects, such as email addresses, which are not entities, but value objects.
Let’s create a Post Entity App/Entity/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 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; } }
The class properties should be the same as the fields in the database table, or you can define them with the @Colum("name"="myfield")
annotation.
What Is a Repository?
The repository allows all your code to use objects without needing 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.
Now it’s time to create the Repository in App/Repository/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 prepareData($data) { return new Post($data); } }
The Doctrine EntityManager
works as the access point for the complete management of your entities.
Then, create 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.index')->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'); } } }
View and routing are the same as usual.
I prefer to create my own Validator based on Laravel’s Validator class. Here’s the Validator App\Validation\PostValidator.php
:
namespace App\Validation; use Validator; class PostValidator { public static function validate($input) { $rules = [ 'title' => 'Required|Min:4|Max:80|alpha_spaces', 'body' => 'Required', ]; return Validator::make($input, $rules); } }
Conclusion
If you have not previously worked with Doctrine 2, I hope this article has been interesting and informative. Laravel 5 does not use Doctrine, but as you can see, there are some packages which allow us to easily use it with Laravel. I created a simple blog app with Laravel 5 and Doctrine ORM, and I hope this can help you to create your desired app. I welcome your comments.
Comments