Symfony and Doctrine 2

Doctrine 2 requires PHP 5.3 so you must have this version in order to follow and test this article.

Today I am happy to tell you that this version of sfDoctrinePlugin is now available and ready for you to use. This article will give you a little information about how you can get started using it today!

Installing

First we need to install the plugin from SVN with the following command from the root of your project:

$ svn co http://svn.symfony-project.org/plugins/sfDoctrinePlugin/branches/1.3-2.0/ plugins/sfDoctrine2Plugin

Now you just need to enable the plugin:

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
    $this->enablePlugins('sfDoctrine2Plugin');
  }
}

Configuring Database Connections

Open your config/databases.yml and configure it for your database connection:

all:
  doctrine:
    class: sfDoctrineDatabase
    param:
      options:
        driver: pdo_mysql
        user: root
        password:
        dbname: doctrine

The DSN(data source name) no longer exists in Doctrine 2. Connection information is much simpler and just supplied as an array of information. You are required to at least specify a driver and the remaining options that are available and required are determined by the driver.

Configuring Your Schema

You can configure your schema much in the same way you always have in config/doctrine. The syntax of the schema files are much different now though. Below is an example of a simple User entity:

# config/doctrine/schema.yml
 
Models\User:
  type: entity
  table: user
  id:
    id:
      type: integer
      generator:
        strategy: AUTO
  fields:
    username:
      type: string
      length: 255
    password:
      type: string
      length: 255

Writing Data Fixtures

The times of using YAML for data fixtures is no longer. Instead, you are only required to use plain PHP for loading your data fixtures.

// data/fixtures/fixtures.php
 
$em = $this->getEntityManager();
 
$admin = new \Models\User();
$admin->username = 'admin';
$admin->password = 'changeme';

Building Doctrine

Now you're ready to build everything. The following command will build models, forms, filters, database and load data fixtures.

$ php symfony doctrine:build --all --and-load

Updating Schema

If you change your schema mapping information and want to update the database you can easily do so by running the following command after changing your mapping information.

$ php symfony doctrine:build --all-classes --and-update-schema

Doctrine 2 In Action

So now that you have everything setup and running lets explore some of the way Doctrine 2 works and how it is integrated with Symfony.

Custom Repository Class

The EntityRepository class is basically what Doctrine_Table is in Doctrine 1. Instead of it being magical and automatic, you simply need to configure your entity mapping information to use a repository class.

Models\User:
  type: entity
  table: user
  repositoryClass: UserRepository
# ...

Now define a UserRepository class somewhere that Symfony can autoload it.

class UserRepository extends EntityRepository
{
  public function getActiveUsers()
  {
    $qb = $this->createQueryBuilder('u');
    $q = $qb->getQuery();
 
    return $q->execute();
  }
}

Now you can use this method like the following:

public function executeIndex()
{
  // ...
 
  $repository = $em->getRepository('Models\User');
  $users = $repository->getActiveUsers();
}

When using Doctrine 2 with Symfony, our entities extend a base class so we have some additional possibilities. You can access the repository methods via static calls, similar to how it is done in Ruby on Rails Active Record.

$users = \Models\User::getActiveUsers();

You also have the same type of built in finders as you do in Doctrine 1.

$user = \Models\User::find(1);
$user = \Models\User::findOneByUsername('jwage');

Entity Manager from Actions

When you are in a Symfony action you can retrieve an entity manager instance by using the getEntityManager() method.

public function executeIndex()
{
  $em = $this->getEntityManager();
 
  $user = new \Models\User();
  $user->username = 'jwage';
  $user->password = 'changeme';
  $user->save();
 
  $em->flush();
}

By default the method returns the entity manager for the last configured database inside databases.yml. You can optionally give an argument for which entity manager to get.

public function executeIndex()
{
  $em = $this->getEntityManager('conn_name');
 
  // ...
}

That is all for now!

This article can't possibly explain everything new about Doctrine 2 so it is recommended that you start reading the documentation on Doctrine 2 from the Doctrine website.

Comments

Nice! Two weeks ago I already gave Doctrine 2 a spin and I must say I'm very impressed. I really can't wait until both Doctrine and Symfony 2.0 are production ready, both API seem so well thought out:-) Really great tools!

Thanks a lot:-D

I though it would be a long wait until have the chance to use Doctrine 2.0 with Symfony!
Excellent jobs guys!
I don't want to be polemic, but I just don't understand why Doctrine is geared to be less magical and with fewer options. It was really needed to get rid of yml fixtures? It was not possible to keep the old fashioned option and encourage (not force) to use the new one?
At the risk of writing "Me too", I'm with @Massimiliano too I'm afraid - using YAML for fixtures was easy to read and quicker to write - is there no way to keep this as an option at all?
The beauty of yml is it is so easy to read.

Using php objects means more chars to type or worse copy and paste.

$admin = new \Models\User();
$admin->username = 'admin';
$admin->password = 'changeme';
$admin->email = 'foo@bar.com';

$user = new \Models\User();
$user->username = 'user';
$user->password = 'pass';
$user->email = 'foo@bared.com';

Models\User:
admin:
username: admin
password: changeme
email: foo@bar.com
user:
username: user
password: pass
email: foo@bared.com

Took me about 2 seconds to write the YAML and much longer to copy and paste and make the changes to the PHP.

One thing I notice when I have tried using Python and Django is the power of writing less. Means you get to the important stuff quicker.

Please please please don't get rid of YML for fixtures...
Is sfDoctrine2Plugin planned for Symfony 1.3 or just for Symfony 2.0 ?
I know that it's not Doctrines fault.... but typing this f***** namespace seperator "\" is really complicated on a german Mac keyboard. I really don't know why the PHP guys choose this. :-)
Do we finally get autocompletion when typing this?
$users = \Models\User::getActiveUsers();
I doubt it because the methods are in the UserRepository class, am I right?
I realize this short article doesn't cover all the changes. I'm just wondering how this is better than Doctrine 1?
@Maik: You're right but noone forces you to use magic, at least not doctrine itself. In pure doctrine 2 you do not have to extend some framework-provided base class and you do not have to use magic static finders and all that. Doctrine2 is no longer ActiveRecord, not one bit.

@Jonathan: To find out whats better (or different), please look through the doctrine 2 documentation. This is not an article about doctrine 2 but about the new sfDoctrinePlugin for doctrine 2 (you can of course use doctrine 2 with symfony without sfDoctrinePlugin).

@Chris: Agreed ;) but its not so bad once you start to make use of the flexible "use" statements. No need to write the full namespaces everywhere.

@Massimiliano: Doctrine (2) is geared towards less magic because it makes code more robust and reliable. We focus much more on less features that are much more stable, rather than the other way around. But nevertheless, doctrine 2 has a lot of features doctrine 1 can only dream of, like class table inheritance, integrated support for optimistic locking and more. Just check the docs. I cant speak for the yml fixtures, that falls under "utilities" and is not really a part of the core functionality. I'm sure Jon will answer that.
The primary reason for no yaml data fixtures in this sfDoctrinePlugin was because, well, we don't have the code to process the yaml for Doctrine 2 yet :) and knowing all the bugs and problems we've had with yaml data fixtures in the past, I thought it would be good to not reintroduce them at all and just use php code. The yaml fixtures have a lot of inherit problems that can never really be fixed. They are just "known problems", not to mention the fact that it is much slower to import a lot of data fixtures when using yaml. The trade off is super simple ease of use for something that works 100% and is stable.

I am pretty sure in other frameworks, like rails they still allow use of yaml data fixtures but discourage it and are slowly moving away from it.
Loading from fixtures is either testing or developing.

In this stage of development I need stuff to get out of my way and stop making me type so much.

YAML is brilliant for this.

But if you guys don't provide a YAML parser I'm sure me and Rich will put one together when the time comes!
if YAML is not the way to go, what about am array type of way to make fixtures. But personally YAML is nice, and is only really in use when developing and or creating new models that needs fixtures :)
Jonathan,

Any reason you guys decided not to go with the interop naming standard that all other major 5.3 frameworks will be relying on?
using php to generate fixtures is ok for me, but what I'd be missing then is the dump-data task. I generally use my generated admins to build objects and then dump them to a fixture.
@Nate Abele Our code had namespaces a long term before any standard had been decided. What is the standard that was decided by the community? We had talked about changing it when it was decided, before the stable 2.0 release but it hasn't come up from anyone yet.
Hi guys, see the following gist for an example that shows how to create a YAML loader for your model. It's very easy:-D

https://gist.github.com/9a677147c42e5fed7d05

Creating loaders this way will do fine for most development environments. In production you'll want to use a sql drop anyway.

- Marijn
@Nate Abele: Too bad that one of the probably largest and earliest 5.3 codebases did not participate in your decision-making (I tried to).
If there are any technical interoperability issues with our standard, please let me know, if its just about lower/uppercase or other cosmetical issues, sorry but no.
Don't get me wrong, the initiative is great, but it should focus on technical interoperability. The most important thing in that sense in my eyes is class loading and the requirement that the namespace+class name reflects the position of the class file in the directory structure. So that anyones autoloader can load anyone else's class files. I am so tired of each library requiring its own autoloader (symfony + symfony components included). Thats what I understand under important standards for technical interoperability.
I am curious about this "interoperability standard". I am a Zend Framework guy but I did not know there was some unification in development.
Hi everyone,

When i use "php symfony doctrine:build-model"

I get this:

PHP Fatal error: Call to undefined method sfDoctrineConvertMappingTask::setConfiguration() in /home/tferreira/tproject/plugins/sfDoctrine2Plugin/lib/task/sfDoctrineBuildModelTask.class.php on line 79

Fatal error: Call to undefined method sfDoctrineConvertMappingTask::setConfiguration() in /home/tferreira/tproject/plugins/sfDoctrine2Plugin/lib/task/sfDoctrineBuildModelTask.class.php on line 79

Can anyone help me please? Thanks
PHP Fatal error: Cannot redeclare class sfDoctrineBaseTask in D:\workspace\SFFU
LL\lib\plugins\sfDoctrine2Plugin\lib\task\sfDoctrineBaseTask.class.php on line 9
1

Me too.

console return:

Fatal error: Cannot redeclare class sfDoctrineBaseTask in D:\workspace\SFFULL\li
b\plugins\sfDoctrine2Plugin\lib\task\sfDoctrineBaseTask.class.php on line 91
If you want to use the annotation mapping in favor of yaml (or perhaps the XML mapping) you can use the configureDoctrineConnection to alter the default doctrine configuration. See the following gist for an example: http://gist.github.com/244558

Comments are closed.

To ensure that comments stay relevant, they are closed for old posts.