Redpill Linpro to host “State of Symfony 2″ conference hub in Oslo

Conference hubs are a way for like minded people to get together and watch/discuss an online conference. One such hub will be hosted in Oslo, Norway by Redpill Linpro (the company I work for). Read Fabien’s blog bost about conference hubs here.

We will be streaming the 10:00 conference on the 22nd June 2010. Refreshments will be served, please give me a shout if you are interested in coming along so we can buy enough conference tickets (we may be less than 5 people otherwise) and arrange a good sized conference room.

Hope to see you there!

Russ.

Using Doctrine 2 with symfony 1.x [Part 1]

Background

It’s been a while since Jonathan announced the availability of Doctrine 2 for Symfony 1.x (wouldn’t recommend trying it with anything less than 1.3) and a a few things have changed since then, so here’s a refresher.

We are at a crossroads now with Symfony 2 looming on the horizon, and many developers may wish to wait until that is more stable (later this year) to make the move to Symfony 2 and Doctrine 2 simultaneously. This is not such a bad idea, however Doctrine 2 is already at a level where you may wish to consider using it in your projects, you won’t be disappointed if you do!

It will take me a while to explain why Doctrine 2 is better than Doctrine 1, and more importantly why you should start using – but you can just take my word for it and take a look at a couple of Jonathan’s presentations to back up my claim. (I Recommend Doctrine 2 – not the same old PHP ORM)

Getting started

Check out the plugin and set up the database as described in Jon’s blog, but don’t configure the schema just yet ;)

We’re going to go all out with the “Doctrine 2″ way of doing things, so we’ll be using annotations, not yaml or xml – although you can look up that syntax if you prefer. (A lot of the stuff below won’t work though – you can’t have multiple yaml files for example yet).

In your project configuration class, you will have access to some methods:

public function configureDoctrineConnection(\Doctrine\ORM\Configuration $config) {}
 
public function configureEntityManager(\Doctrine\ORM\EntityManager $em) {}

In the first of these, you need to add all the things to configure Doctrine before the entity manager can be created. Once Doctrine has created the entity manager, you can use the second method if you need to, to further configure the entity manager. (Registering a Doctrine listener for example).

So, in your configureDoctrineConnection() method, you’ll want to do something like this:

// Decide where you want the proxy classes to be stored, and which namespace they should use
$config->setProxyDir(sfConfig::get("sf_cache_dir")."/Proxies");
$config->setProxyNamespace("Proxies");
 
// You may want to make this environment specific for performance reasons
$config->setAutoGenerateProxyClasses(false);
$config->setSqlLogger(null);
 
// This will get things working, but later you will want to use APC
// or another "real" cache for production
$cache = new \Doctrine\Common\Cache\ArrayCache;
$reader = new \Doctrine\Common\Annotations\AnnotationReader($cache);
$config->setMetadataCacheImpl($cache);
$config->setQueryCacheImpl($cache);
 
// Tell Doctrine where to find your entities (you may have more than one location)
// This is mostly required for cli tasks that iterate over all of your entities
$paths = array(sfConfig::get("sf_lib_dir") . "/Entities"); // Populate this with all the locations of your entities
$reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\');
$annotation = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, $paths);
$config->setMetadataDriverImpl($annotation);
 
// Register all the classes that Doctrine needs to autoload
$classLoader = new \Doctrine\Common\ClassLoader('Entities\doctrine');
$classLoader->setIncludePath(sfConfig::get('sf_lib_dir'));
$classLoader->register();
 
$classLoader = new \Doctrine\Common\ClassLoader("Proxies");
$classLoader->setIncludePath($cachedir);
$classLoader->register();
 
$classLoader = new \Doctrine\Common\ClassLoader("Another\Namespace");
$classLoader->setIncludePath(sfConfig::get('sf_lib_dir'));
$classLoader->register();

Ok, that was a lot of stuff – but Doctrine 2 is more verbose – meaning less magic, but more explicit code. Some of the above calls may not be necessary – I need to do further testing, and also I’d like to get a lot of these things “standardised” in the plugin, so you can skip a lot of this if you follow a “default” path. The problem with the original release of the plugin was that it was not possible to have multiple class dirs, which makes it impossible to have plugins (for example) that also contain Entities. With the approach above, you explicitly add as many classes as you like to the annotation driver and the autoloader, and your plugin configuration classes can do the same.

Active Entity

The plugin comes bundled with Active Entity – this means that by extending ActiveEntity from our model classes (Entities) we can use our classes in much the same way as in Doctrine 1. Things like the following become available:

$user->toArray();
$user->save();
\Entities\User::find(1);
$user["name"];

ActiveEntity was written and included with good intentions, because for Doctrine 2 to continue to be compatible with symony in the same way as Doctrine 1, a bit of magic needed to be re-introduced. BUT – for anyone that has been excited by Doctrine 2, one of the major breakthroughs is the non-intrusive model, and ActiveEntity kills that (along with a kitten):

//With ActiveEntity:
class \Entities\User extends  \DoctrineExtensions\ActiveEntity {}
 
//Without ActiveEntity:
class \Entities\User extends-or-does-not-extend  \Whatever\The\Hell\You\Like {}

So I strongly recommend that you Don’t extend ActiveEntity – and gradually the plugin will be “fixed” so that is never necessary. Currently basic object forms and some widgets are working fine without it, but I haven’t tested it with admin generators or anything like that yet so feedback is appreciated.

If you follow my advice, you have to do everything the “Doctrine 2 way”. That means taking control of your own code! Write your own getters and setters, extend your own classes (if you want to) – be verbose, write OOP code, etc!

Ongoing development

Doctrine 2 is still in Alpha at the moment and is constantly changing. Since we are using it actively in a project at the moment, we normally spot the changes pretty quickly and update the plugin to keep up, however we are not using all the aspects of the plugin (like generators, all the form widgets, etc) so from time to time something might completely fail when you svn up.

We are also trying to slowly, and safely (for BC) remove the need for ActiveEntity – but we’re being careful with this because we don’t want to break existing projects that are using it.

Report bugs in the usual way using the symfony bug tracker (if it’s plugin related) or on Doctrine Jira (if you know it is a Doctrine issue). When reporting plugin bugs, please register the bug under the sfDoctrine component and add the keyword sfDoctrine2Plugin as I use a filtered query to keep an eye on Doctrine 2 related issues.

Coming in part 2…

Will either be based on feedback/suggestions (if any) or I’ll move on to some real world examples and best practices when it comes to integrating symfony and Doctrine 2.

Is symfony the best documented OS project out there?

Introduction

Well, I’m not actually going to answer that question because there are many thousands of projects and I don’t actually know how well they are all documented, so it would be completely unfair of me! However, with the release of this year’s advent calendar symfony is at least setting a high standard for others to follow.

So what should you read?

In our office, we have several symfony books kicking around, and new developers often ask where to start. In fact, it can be quite frustrating to have to tell a new guy that the 2 days they’ve spent studying the “Definitive guide to symfony” has really only helped them to maintain our first project from 2 years ago…

Starting a new project & don’t know much about symfony

  • Do the Jobeet tutorial – seriously, no matter how much you think you can wing it, don’t. Set aside a couple of days and go through it. It’s even supported by an svn repository so you don’t have to type much code if you don’t want to – although be aware that the documentation is much more up to date that the code behind it, so if in doubt, trust the doc. The tutorial is also available in the Propel flavour.
  • Scan the reference guide - I’m not saying read it, unless you have bags of time, but at least give it a quick scan so you know what’s there. When you’re struggling later you’ll think “I’ve seen that somewhere” – and chances are it was in the reference guide

Know the basics and want to take it further

  • Read the forms book - The newer versions of symfony still support the forms framework and chances are you will want to use it, so it’s a good idea to give this a read. You will have a good idea already after doing the  Jobeet tutorial but there are things you will have missed and this will open your eyes.
  • Read More with symfony (the 2009 advent calendar) - If you’ve started to get the hang of things and want to know what “the experts” do when they are coding with symfony, this is where to look! This book has been written by members of the symfony community, so comes totally from the perspective of experienced developers who have been through the same process as the one you are going through.
  • Browse the cookbook – If there is something you are wondering about that seems like it would be a common task, there might well be a “recipe” for that very task! The cookbook has not made it to the 1.4 branch of documentation at the time of writing, but most of the recipes (if not all) will work just fine in the later versions. 
  • Check the wiki - this is basically a cookbook with user supplied recipies, whereas the “official” cookbook is maintained by the core team. That’s not to say it’s any less of a resource – just that it’s slightly more likely to contain “opinions” and out of date ideas.

Stuck with something

  • Check through all of the above – in particular the reference guide and cookbook. “Googling” for a well structured phrase will often get you to one of the correct pages in the manual – just be sure to switch it to the correct version, as a lot of the old 1.0 pages still come up first in searches.
  • Browse the API documentation - The symfony codebase is generally very well commented, so if you are wondering about the methods of a particular class, or the options for a widget, or some other property/function/value that you can’t find anywhere in the documentation, there is a good chance the api docs will help you out.

Getting “human” help

Sometimes all the reading and googling gets you nowhere and you can quickly find yourself feeling very frustrated and alone. Fortunately symfony is backed by an enourmous community, and in a rare web-community-twist it’s one that works both ways. Not only do people ask questions, but there are hundreds of people around to answer them too!

My personal “hangouts”

  • IRC – Need a quick answer, this is the place.  You won’t always get a quick answer, because there are a lot of lurkers, but there are also a lot of “regulars” who hang around in there so be patient and try again a bit later if you don’t get a response straight away. Check out freenode channel #symfony (I’m rooster).
  • Symfony forum - Not quite as well publicised as the Google groups, but a valuable resource all the same. It’s really down to your communication preferences if you like forums or mailing lists, so just pick one (or both). What you will find is that different people are active on the forum compared to the Google group – it’s a shame there is not much harmony here as it can be a bit strange for newcomers. One thing I will say about the forum is that you will always get a response, if not by anyone else at least by Halfer, Ryan or myself (Russ).
  • Google symfony users group - Very busy but with a lot of experienced developers around to answer questions. Can take a bit longer than the forum to get a response, but you are generally speaking to a larger audience here. You are more likely to get a response from Fabien if you post here, he hasn’t posted in the forum for 6 months! I’m RussMonkey in this one.
  • Trac - If you have to come to the conclusion that what you are working with is a bug, or maybe it took you way to long to find the answer because the documentation on the subject was wrong, missing or just bad, then please raise a ticket. Depending on the nature of the issue, it can seem like it takes a while to get a response – so rather than “bumping” comments in the ticket itself, it’s normally best to start a discussion in an appropriate place, most likely the dev mailing list and be sure to link to the discussion from the ticket, and link to the ticket in the discussion.

Staying “fresh”

It’s quite hard to keep up with everything that’s going on, so there are a few things that you really should do:

  • Subsribe to the symfony project blog - this is where you’ll get the most important news about changes and future development from the core team.
  • Subscribe to the symfony bloggers feed – blog posts from the community, including this one, appear here. It has grown quite substantially lately, and can get cluttered with non-English blogs, which really should be in a seperate feed to be honest – but if you are using a feed reader you can just skip over them (unless you can read that particular language of course). There are a lot of experienced bloggers here, and you’ll start to recognise some of the more useful ones as you read.
  • Subscribe to the user group mailing list - either as a daily digest, or an rss feed (my preference). It’s useful to scan through and see what people are talking about, so you can recognise recurring problems and issues that you may face one day.
  • Those above things should take no more than a few minutes a day to browse and keep an eye on, but if you really want to get a bit more involved and have a bit more time to spare, then consider IRC, the forum and the dev mailing list also. 

Summary

So, you can see why I am starting to believe that symfony is at least one of the best documented and supported projects out there, in this post alone I have listed 14 resources that the developer can use can make the most of their time developing. Symfony has a (somewhat improving) reputation for being a one man show when it comes to the direction of the project and the core coding, but that is certainly not the case when it comes to the community support side of things – you are not alone!

PHP Session settings ignored?

I just read an extremely interesting post which explains an unwanted side effect of garbage collection by some Debian based systems. This cleanup process causes (maybe amongst other things) sessions to be cleared every 24 minutes, regardless of any ini setting you have set after the php.ini file is parsed (for example using ini_set() – if you are a symfony user, this is done during initialisation based on a setting in your factories.yml file).

Check it out: http://elephpants.blog.linpro.no/2009/10/02/unwanted-end-of-sessions/

Symfony forms – Flexible widgets based on user credentials (sfcontext is evil)

Background

This issue has come up many times in the symfony forum and on IRC, and whilst it seems like a fairly trivial one, it is important to discuss the best practice around it. Often we want to modify a form based on something outside the form’s scope, like a user’s credentials or the page they are on, or maybe some session values – lets take the example of a dropdown widget where admin users get to see a few more options.

The bad way

Everyone that has been using symfony for a while is aware of the context singleton. This magic sprite allows us to grab all sorts of information about the context we are in, including the user, session, request, view, and many more things. Whilst it certainly has its place, most of the time, however, we should avoid using it. The clue to the reason why is carefully disguised in the class name “Context”.

When we refer to a context in our code, we are locking ourselves in to the fact that the context must exist, so every time we use it we are basically saying that this class can now only be used in a symfony project with a fully initialised symfony stack hanging over it. This becomes a problem in things like unit tests, where you have to mock up a loaded context object with bells and whistles in order to test a simple function or class.

So this is what you might think of doing:

// In your form class
public function configure()
{
  $choices = array(1 => "something boring", 2 => "something dull");
 
  $currentUser = sfContext::getInstance()->getUser();
 
  if ($currentUser->isAuthenticated() && $currentUser->hasCredential("admin"))
  {
    $choices += array(5 => "something cool", 6 => "something leet");
  }
 
  // ....
}

The reason this is bad is that the forms framework is a standalone framework – it should be possible to pick up your form class and drop it into any project. It should also be possible to test it independently of symfony, without being tied in by sfContext. So what is the better way?

The dependency injection approach

You might read that and think “woah, this is getting complicated!” but we’re not talking about dependency injection containers here, we’re simply saying that you can make your form object depend on something to run. The thing it depends on should not be the context singleton, it should be the minimum thing that the form needs to operate correctly – which in this case, is a user object that supports credentials.

// In your actions.class.php
$this->form = new myForm(array(), array("currentUser" => $this->getUser()));
 
// In your form class
 
// ...
if ( !($this->getOption("currentUser") instanceof sfUser))
{
  throw new InvalidArgumentException("You must pass a user object as an option to this form!");
}

The test here is where we are making this form “dependent” on a user object. In this case we are insisting that the object is an instance of sfUser, which you may argue is tying us in to symfony again, but you could use any test here to ensure that the object will have the necessary functionality you need, maybe check for the existence of a “hasCredential()” method for example.

When writing a test for this form class, we now only need to instantiate a user object and load it with some credentials – much easier than doing the same thing and locking into a context singleton. There may be other times when this form could be useful in a lightweight environment, where you can get speedy access to a user object but don’t want the overhead of the symfony context – you might not think of one now, but it’s best to code this way and you’ll have less reasons to kick yourself further down the line.

The completed code, for our single widget form

// In your form class
public function configure()
{
  if (!($this->getOption("currentUser") instanceof sfUser))
  {
    throw new InvalidArgumentException("You must pass a user object as an option to this form!");
  }
  else
  {
    $currentUser = $this->getOption("currentUser");
  }
  $choices = array(1 => "something boring", 2 => "something dull");
 
  if ($currentUser->isAuthenticated() && $currentUser->hasCredential("admin"))
  {
    $choices += array(5 => "something cool", 6 => "something leet");
  }
 
  $this->widgetSchema['my_dropdown']    = new sfWidgetFormChoice(array("choices" => $choices));
  $this->validatorSchema['my_dropdown'] = new sfValidatorChoice(array("choices" => array_keys($choices)));
}

Wrap up

Think about this example the next time you think about modifying a query based on a user object, or session value in a peer class, or Doctrine table class… Maybe you should have passed a parameter there too? Every class and method you write, think about how you can reuse it, will it even be possible the way you have written it? If something simply must be coded in a symfony specific way, think about making a parent and a child class for the problem you are trying to solve. In the parent class, you can make things as generic as possible – so you can re-use that class to your heart’s content. In the child class, you can add the symfony specific code – kept to a minimum.

Next Page »