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.
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.
@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