Using factories for your Doctrine entities

Old-school data storage, although you have to watch out for free-floating, full-torso, vaporous apparitions.

With it's usage of simple PHP objects for entities, Doctrine is a very easy-to-use ORM. It instantiates these entities using reflection. If you want to use entities that rely on another object via dependency injection, however, then you're stuck with setter injection, which can be quite painful as you'd need to apply it every time you created an entity, and even worse when trying to apply it to related entities as it leads you towards dependency hell.

Granted, there aren't many times that you'd need to use dependencies with your entities, but there are a few where you'd want to make these changes in your entity instead of another layer, such as handling automatic hashing of data in and out of your database layer, or validation.

This following code shows how to use a simple doctrine plugin to pull new entities from a factory instead of reflection, allowing constructor injection on your entities and nice, clean, testable code.

The factory

The factory objects are our saviours from the possible dependency hell mentioned above. We can instantiate these once only using Symfony's services or Laravel's service providers with the only dependencies they require, and then just call them later.

An example factory may look like the following code. This assumes you've already run composer require dittto/doctrine-entity-factories:

namespace App\Entities\Factories;

use App\Entities\TestEntity;
use Dittto\DoctrineEntityFactories\Doctrine\ORM\Mapping\EntityFactoryInterface;
use Illuminate\Contracts\Validation\Validator;

class TestEntityFactory implements EntityFactoryInterface
{
    private $validator;

    public function __construct(Validator $validator)
    {
        $this->validator = $validator;
    }

    public function getEntity()
    {
        return new TestEntity($this->validator);
    }
}

These factories can do pretty much anything you want, but they must implement EntityFactoryInterface so we know how to get a new entity from them.

Upgrading Doctrine

For this blog, we're going to cover how to do this in Symfony 3, as it's really, really easy. The codebase's readme file contains instructions on how to also do this with Laravel 5.

First up, we're going to tell Doctrine to use our way of mapping entities instead of it's own:

# app/config/config.yml
doctrine:
    orm:
        entity_managers:
            default:
                class_metadata_factory_name: Dittto\DoctrineEntityFactories\Doctrine\ORM\Mapping\ClassMetadataFactoryWithEntityFactories

For the next part, we're going to override how Doctrine managing entities. The mapping code above allows us to add entity factories to it, but if we just use that then we've no easy way to add them. The following code adds a decorator that allows us to add those factories:

services:
    dittto.doctrine_entity_factories.entity_factory_manager_decorator:
        public: false
        class: Dittto\DoctrineEntityFactories\Doctrine\ORM\Decorator\EntityFactoryManagerDecorator
        decorates: doctrine.orm.default_entity_manager
        arguments: [ "@dittto.doctrine_entity_factories.entity_factory_manager_decorator.inner" ]
        calls:
            - [addEntityFactory, ['App\Entities\TestEntity', '@app.entities.factory.test']]

    app.entities.factory.test:
        class: App\Entities\Factories\Testfactory
        arguments: ['@validator']

And that's it. If you now try and do something that makes Doctrine create a TestEntity, you'll get an object that was created using a factory instead of reflection.

If you want to look at the source code for this, or see how to do this in Laravel, then go to github.com/dittto/doctrine-entity-factories.