Domain-Driven Design Repositories with Doctrine ORM and ODM in Symfony

Welcome to my first Domain-Driven Design in PHP post.

DDD in PHP

The following code is inspired by the Domain-Driven Design in PHP book written by Carlos Buenosvinos (@carlosbuenosvinos) and Keyvan Akbary (@keyvanakbary):

Value Objects

My examples code include Value Objects for the identifiers of Aggregate Roots (AR) For now they will be created manually. Later we will see how to use custom Doctrine DBAL types to persist them.

Interface

Doctrine Repository

ORM

ODM

The ODM implementation (e.g. MongoDB) looks very similar:

Service

Mapping

I recommend to use XML mapping for your Domain Models. This way your class remains POPO and does not mix ”’infrastructure”’ with ”’domain”’ concerns.

Feel free to join the discussion about this topic on GitHub:

Document

Here is an ODM Document example:

Entity

For those who still like to use annotations here is an example Entity:

Usage

Handler

Controller

Naming Conventions

Feel free to join the discussion about naming repositories and other best practices on GitHub.

 

Using PHP YAML Extension and Unicode on Entity column with Doctrine Events

I have a Rails legacy database that uses a YAML formatted array in a column. My new App is based on Symfony2 and Doctrine2.

To make this column useable in Sonata Admin I had to (re-)convert between PHP Array and YAML.

 

This worked fine using Doctrine Events:

 

Note the YAML_UTF8_ENCODING constant when emitting. By default yaml_emit uses the YAML_ANY_ENCODING constant.

All constants can be found here:

http://php.net/manual/en/yaml.constants.php

How to inject Symfony Security into Doctrine Event Listener without Circular Reference Exception

As of Symfony 2.6, you can inject the security.token_storage into your listener. This service will contain the token as used by the SecurityContext in <=2.5.

 

 

Backlinks:

How to use CHAR instead of VARCHAR with Doctrine ORM

 

Single and Multiple Ordering To-Many Associations in Doctrine2

Associations in Doctrine2 can be ordered by default without using an extra query:

 

This also works for multiple ordering:

 

Backlinks:

Populate resp. set default values on form resp. object or instance in SonataAdminBundle

To set default values on a new instance you can override the getNewInstance() method in your Admin class:

This is a pretty basic example for setting columns that expect a string, bool etc.

If you need to set an related Object you can not simply pass an identifier (e.g. company_id). You will have to fetch the instance:

Fetching a complete Object actually causes some overhead. Instead it is recommended to only fetch the reference:

 See Doctrine documentation for details.

If you need to get the identifier from the Request you can use the getRequest() method:

 

The featured parts of the User Entity:

 

Backlinks:

FOSUserBundle login error ‘Could not convert database value “” to Doctrine Type array’

Refactoring an existing project and migrating to Symfony2 I had to import 4000 users into the FOSUserBundle table fos_user.

I wrote a little import script that unfortunately updated some columns with an empty string. These columns were used by Doctrine for storing arrays:

  • roles (DC2Type:array)
  • facebook_data (DC2Type:json)
  • twitter_data (DC2Type:json)
  • gplus_data (DC2Type:json)

After trying to login I got the following error:

Could not convert database value “” to Doctrine Type array

The solution was to replace the empty string with the expected array e.g.:

If you want to add Roles you can use the FOSUserBundle command line tools:

FOSUserBundle Command Line Tools:

https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/command_line_tools.md

 

Backlinks:

http://stackoverflow.com/questions/8818387/doctrine2-conversion-error

How to use orderBy param on find_method findAll with Doctrine ObjectSelect

The docs offer an example for an extended version of the DoctrineModule\Form\Element\ObjectSelect:

This find_method parameters allow you to order and sort the result of your repository method.

 

Unfortunately this will not work when using the findAll method. But why?

Looking at the source code of the Doctrine ORM you can see that unlike the findBy method findAll does not allow passing arguments for filtering, sorting nor limiting:

https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/EntityRepository.php#L157-182

 

But there is a workaround:

Use the findBy() method instead and simply pass an empty array for the criteria parameter.

 

That’s it!

 

Backlinks:

Doctrine Debug dump usage instead of var_dump

Lazy load proxies always contain an instance of Doctrine’s EntityManager and all its dependencies. Therefore a var_dump() will possibly dump a very large recursive structure which is impossible to render and read.
You have to use Doctrine\Common\Util\Debug::dump() to restrict the dumping to a human readable level.
Additionally you should be aware that dumping the EntityManager to a Browser may take several minutes, and the Debug::dump() method just ignores any occurrences of it in Proxy instances.

See tutorial: http://docs.doctrine-project.org/en/2.1/tutorials/getting-started-xml-edition.html#a-first-prototype