Laravel & Doctrine Tutorial 2

I wrote an article a while back on Laravel and Doctrine, since then a new library for Laravel and Doctrine integration has appeared and it’s pretty good.

Installation

– To install the Laravel Doctrine package for Laravel 5.2 add the following to composer under require:

laravel-doctrine/orm:1.1.*

– Add

LaravelDoctrine\ORM\DoctrineServiceProvider::class,

to

config/app.php

under the service providers section. – Run this command to publish the config settings for Doctrine:

php artisan vendor:publish --tag=“config”

First Model

I prefer using Yaml mappings for my models. By default Laravel Doctrine will look for them in the app directory which I don’t like. I put my models in a subdirectory called models, in that directory I put a subdirectory called mappings to store the Yaml files. To tell doctrine to look for these files in my custom path I have to update the Doctrine configuration to with this:

    'paths'      => [
        base_path('app'),
        base_path('app') . '/Models/mappings',
    ],

In the YAML mappings directory create the following file

App.Models.User.dcm.yml

and add the following content:

App\Models\User:
  type: entity
  table: users
  id:
    id:
      type: integer
      generator:
        strategy: AUTO
  fields:
    name:
      type: text
    email:
      type: text
    password:
      type: text
    rememberToken:
      type: text

In the Models folder create this file

User.php

and add the following content

namespace App\Models;
use JsonSerializable;

class User implements JsonSerializable
{
    use JsonSerializer;
    
    public $id;
    public $name;
    public $email;
    public $password;
    public $rememberToken;

    private $__hidden__ = ['password', 'rememberToken'];
    
    /**
     * User constructor.
     * @param $id
     * @param $name
     * @param $email
     * @param $password
     * @param $rememberToken
     */
    public function __construct($id, $name, $email, $password, $rememberToken)
    {
        $this->id = $id;
        $this->name = $name;
        $this->email = $email;
        $this->password = $password;
        $this->rememberToken = $rememberToken;
    }
}

Two things that stands out about this file

  • The fields are public
  • The JsonSerializer

The fields are public because I feel that the getter-setter pattern isn’t relevant anymore even in languages like Java. In the Java the getter returns, in many cases, a reference to a private variable rather than a copy, breaking whole reason for the pattern. The JsonSerializer is a trait I wrote to exclude some fields from being returned by for example: an API. It excludes the Doctrine proxy custom fields as well. In this case the password and remember me tokens are excluded similar to Eloquent.

namespace App\Models;

trait JsonSerializer
{
    /**
     * @param  int    $options
     * @return string
     */
    public function toJson($options = 0)
    {
        json_encode($this->jsonSerialize(), $options);
    }

    /**
     * Specify data which should be serialized to JSON
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
     * @return mixed data which can be serialized by json_encode,
     *               which is a value of any type other than a resource.
     * @since 5.4.0
     */
    public function jsonSerialize()
    {
        $vars = get_object_vars($this);

        $r = [];
        if (isset($this->__hidden__)) {
            foreach($vars as $k => $v) {
                if (!starts_with($k, '__') && !in_array($k, $this->__hidden__)) {
                    $r[$k] = $v;
                }
            }
        } else {
            foreach($vars as $k => $v) {
                if (!starts_with($k, '__')) {
                    $r[$k] = $v;
                }
            }
        }

        return $r;
    }
}

Create a repository

Thanks to Laravel’s ability to inject services without providing a ServiceProvider this step is relativity simple. The two thing to note is the automatic binding of a entity manager by Laravel (The doctrine plugin already created one for us) and how the EntityRepository constructor is called within our constructor.

?php

namespace App\Repositories;

use App\Models\User;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;

class UserRepository extends EntityRepository
{
    /**
     * @var EntityManager
     */
    private $em;

    /**
     * PageViewRepository constructor.
     * @param EntityManager $em
     */
    public function __construct(EntityManager $em)
    {
        $this->em = $em;
        parent::__construct($em, $em->getClassMetadata(User::class));
    }
}

Creating a service

Like before because Laravel knows how to construct a Repository object because it knows how to inject a entity manager we can create a service level API object without providing a ServiceProvider to instantiate an object.

?php

namespace App\Services;


use App\Repositories\UserRepository;
use App\User;

class UserService
{
    /**
     * @var UserRepository
     */
    private $userRepository;

    /**
     * UserService constructor.
     * @param UserRepository $userRepository
     */
    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function findAll()
    {
        return $this->userRepository->findAll();
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *