Symfony Resources Central

Web development made simple

Wednesday, April 30 2008

Overview of symfony 1.1 event dispatcher

As you may now by now, Symfony 1.1 introduces a new powerfull event dispatcher inspired by Apple Cocoa's NotificationCenter. Basically, it allows any entity to "listen" to events, and get a call on the registered callback if this event ever happens.

Symfony 1.1 provides some default events you can listen to, but of course you can create your own events if you need.

Listen to an event

To listen to an event, you need to use the "connect" method on the event dispatcher instance. The first parameter is the event name, and the second is a PHP callable that will get called if the event happens.

$dispatcher->connect('user.change_culture', array($this, 'listenToChangeCultureEvent'));

Create a custom event

To use the dispatcher for your own needs, you just need to define your event name in your project specifications, and send notifications to it. Depending on the behaviour needed, three options are offered:

Simple notifications

The simpliest way is to notify all listeners with the ->notify method.

$dispatcher->notify(new sfEvent($this, 'my.super.cool.event'));

Notifications until something

Sometimes, you prefer to notify all listeners until one says "Ok guys, I handled this one. Don't worry about it anymore".

$dispatcher->notifyUntil(new sfEvent($this, 'my.super.cool.event'));

The first listener that will return non-false value will stop the event chain.

Filtering notifications

The last notifying method is called filtering. You set this up when you want to permit anything to act as a filter on something, meaning any listener can modify a source object.

$dispatcher->filter(new sfEvent($this, 'my.super.cool.event'), $objectToFilter);

Every listener must return the filtered value (or the original object if nothing was done) to pass to the next listener.

Practical use: Register routes in your plugins

One of the first practical applications that came to me was the new way of registering routes in plugins. In symfony 1.0, a coincidence made possible to use the routing in a plugin's config.php but that's not possible anymore in symfony 1.1, so you have to use the event dispatcher. To accomplish this, we're going to set up a routing.load_configuration listener in the plugin's config.php:

$this->dispatcher->connect('routing.load_configuration', array('myPluginRouting', 'listenToRoutingLoadConfigurationEvent'));

Then you just need to create the callback class/method:

class myPluginRouting
{
  /**
   * Listens to the routing.load_configuration event.
   *
   * @param sfEvent An sfEvent instance
   */

  static public function listenToRoutingLoadConfigurationEvent(sfEvent $event)
  {
    $r = $event->getSubject();

    // preprend our routes
    $r->prependRoute('my_route', '/my_plugin/:action', array('module' => 'myPluginAdministrationInterface'));
  }
}

Here we go :-D

Monday, April 21 2008

Don't be fooled by awkward view.yml js/css positionning syntax!

Short post today about advanced view.yml configuration (in symfony 1.0, and 1.1) for assets.

You can give additional options to "javascripts:" and "stylesheets:" sections, but the sin equa non condition is to know about the yet very un-documented view.yml assets syntax.

So here it is:

  javascripts: [ jquery: { position: first } ]
  stylesheets: [ mycss: { position: last, media: screen } ]

I don't know if there are others options like thoose available, but taken the 'position' attribute apart, which is extracted to become the $position method argument of sfWebResponse::addJavascript() and ::addStylesheet, any other option is passed in the $options array.

Methods prototypes below:

class sfWebResponse ...
{
  /* ... */
  public function addJavascript($js, $position = '', $options = array());
  public function addStylesheet($css, $position = '', $options = array());
  /* ... */
}

Monday, December 24 2007

Creating custom config handlers in symfony 1.0

When writing plugins, you often want to do it the symfony way, by putting configuration stuff in YAML files to allow the final user to override your default values in a harmless way.

To do this, the correct way is to create a config handler, extending sfConfigHandler, or its child class sfYamlConfigHandler.

Here is a simple one, just dumping YAML data found in your config file into an sfConfig::get()-able PHP dataset.

class myStupidConfigHandler extends sfYamlConfigHandler
{
  public function execute($configFiles)
  {
    // retrieve yaml data
    $config = $this->parseYamls($configFiles);

    $code = sprintf("<?php\n" .
                    "// auto-generated by myStupidConfigHandler\n" .
                    "// date: %s\n" .
                    "sfConfig::set('my_stupid_config_entry', %s);",
                    date('Y-m-d H:i:s'), var_export($config, 1));

    return $code;
  }
}

We could enhance it a bit, to take advantage of symfony's environments and permit an 'all' (as a default section) and as many environment specific sections as you want. Here is it.

class myStupidConfigHandler extends sfYamlConfigHandler
{
  public function execute($configFiles)
  {
    // retrieve yaml data
    $config = $this->parseYamls($configFiles);

    // get current environment
    $environment = sfConfig::get('sf_environment');

    // merge default and environment specific config
    $config = sfToolKit::arrayDeepMerge(isset($config['all'])?$config['all']:array(), isset($config[$environment])?$config[$environment]:array());

    $code = sprintf("<?php\n" .
                    "// auto-generated by myStupidConfigHandler\n" .
                    "// date: %s\n" .
                    "sfConfig::set('my_stupid_config_entry', %s);",
                    date('Y-m-d H:i:s'), var_export($config, 1));

    return $code;
  }
}

Next step will be to tell symfony which configuration file patterns should be loaded using our class. To do this, add the following entry to config/config_handlers.yml (whether in your app, plugin or module, depending on the scope of your config handler)

config/stupid.yml:
  class:    myStupidConfigHandler

Once this is done, the very little remaining last step is to mak sure you include the compiled yml.php file with the following magic command, that will rebuild it when unexistant or outdated:

require_once(sfConfigCache::getInstance()->checkConfig('config/stupid.yml'));
/* ... */
$stupid_config = sfConfig::get('my_stupid_config_entry');

easy one :-)

Tuesday, October 30 2007

Using DBMS functions with sfDoctrine

I recently had a peek on symfony forum, and seen someone asking "How can I make a SELECT count(*) FROM .... with doctrine?". An answer to such a question should be pretty obvious as it's of everyday use, but the question seems to have come to life many times as well on IRC than on the forum/mailing list. Here is my two-cents how-to.

Continue reading...

© Copyright 2007-2008 daKrazy. All rights reserved.

Design and template by hartym