Kris Wallsmith

Discussing web development, Symfony and fatherhood.

Posts tagged symfony

Jul 22

Doctrine and MySQL integers

I just pasted this somewhere handy for my own reference. It’s the logic Doctrine uses to translate the integer data types you specify in schema.yml into a MySQL data type. If you’ve ever wondered why the length you set on an integer isn’t directly translated to the table definition used in your database, here’s why.

For example, the id columns in sfDoctrineGuardPlugin are defined as integer(4), which translates to id INT in the table definition. Where did the 4 go? Now you know…


Jul 18

Symfony: Denote Required Form Fields

You can checkout the full gist for this tutorial here.

The symfony form framework separates a form’s presentation and validation into two distinct collections of classes: widgets and validators. For the most part, these two codebases live happily without any knowledge of eachother. When data from the validators needs to be shown in the presentation layer, such as in the case of error messages, symfony provides the sfFormField class, which bridges this divide.

One common requirement for web forms is that required fields must be apparent to the user. Implementing this in the symfony form framework may not seem possible, because of the divide between widgets and validators, but it is in fact quite possible, and easily implemented.

This is a quick tutorial on how to do just that.

Validator, meet Widget

The first step is for the form’s validators to inform the form’s widgets which of its fields are required. We can do this by passing a array of field names to the widget schema once the form is fully configured:

The getRequiredFields() method returns a simple array of formatted field names that corresponds to those validators marked as required. It accomplishes this by recurrisively iterating through the form’s validators:

Label Formats

Once the form’s widgets know which fields are required, it’s up to the form formatter to decorate those fields accordingly. I’ve done this using an extension of the standard table formatter class:

Finally, we just add this custom formatter to our forms by adding the following to the overloaded __construct() method from above:

Your forms’ required fields should now render with labels that include the required class. Of course, you can do something different in the formatter to suit your needs, add a * or add a class to the entire row, but hopefully the implementation should now be clear.


Jul 10

Managing Master and Slave Database Connections with symfony and Doctrine

This is one of those things that should be much easier than it is. Since I started using Doctrine a few months ago, I’ve been impressed with how complete it is, but I can definitely see room for improvement as the project matures. Setting up read and write connections is one of those areas.

My challenge was to get the project I’m on ready to be hosted on Amazon EC2, with the help of RightScale. If your hosting on the cloud and have the funds available, check out RightScale. They have a number of server templates with best practices already implemented, and great online tutorials.

The environment I’m setting up includes one master and (potentially) multiple slave MySQL servers (setup with EBS storage). Setting up these servers was a piece of cake, thanks to the heavy lifting RightScale had done for me with their server templates and tutorials. The challenge I met was in getting symfony (1.2) and Doctrine (1.1) setup to choose the right connection for each query.

Code Slingin’ Below

The first order of business is organizing the database connections. I decided to go with a simple naming syntax and assume the master connection is named master and the slave connection names begin with slave. I added these methods to ProjectConfiguration so these connections are accessible:

Now I can easily grab the master connection by calling ProjectConfiguration::getActive()->getMasterConnection() from anywhere in my project, and get a slave connection by calling ->getSlaveConnection(). This is cool, but it turned out to be the easiest part, by far.

Smartly Forcing a Master or Slave Connection

I started with a tutorial on master and slave connections in the Doctrine documentation repository. This was easy to implement, but it’s not a complete solution. There are write queries in the Doctrine core that don’t go though either Doctrine_Query::preQuery() or Doctrine_Record::save(), so they end up using the current connection, which is usually the slave connection.

I came up with a solution, but I’m witholding judgement on how stable it is. I’m using Doctrine events to filter all queries run through Doctrine and swap out the PDO object used inside the Doctrine_Connection object with either the master or slave PDO object:

Then simply add this listener to each of you Doctrine_Connection objects by using the (undocumented?) ProjectConfiguration::configureDoctrineConnection() method:

This seems to be working for my purposes, but I feel a bit dirty hacking into Doctrine_Connection objects like this. For a brief moment as each query is run, the Doctrine_Connection object that includes the name master may in fact include a slave PDO object, and vice versa.

One More Thing

This new environment includes multiple database servers, so it naturally includes multiple load-balanced web servers, which precludes the use of sfSessionStorage because of it’s reliance on a local filesystem. I decided to go with sfPDOSessionStorage for the time being.

I was expecting to have to extend sfPDOSessionStorage to choose between master and slave connections for each storage operation. However, upon looking at the code I realized that every operation may possibly include a write query, so I just specified the master connection in factories.yml:

NOTE This configuration assumes there is always a connection named master, whereas the method in ProjectConfiguration includes a fallback to the current Doctrine connection.


Jul 5

Doctrine Timestamps and User Timezones

I recently added a “timezone” dropdown to the user preferences screen on a symfony application currently in development. This simple extension to the sfDoctrineRecord class makes it easy to present times from the database in the current user’s timezone.

abstract class myRecord extends sfDoctrineRecord
{
  protected function _get($fieldName, $load = true)
  {
    if ($value = parent::_get($fieldName, $load))
    {
      $column = $this->getTable()->getColumnDefinition($fieldName);
      if ($column && 'timestamp' == $column['type'])
      {
        $timezone = date_default_timezone_get();
        if (ProjectConfiguration::DEFAULT_TIMEZONE != $timezone)
        {
          // shift value to the current timezone
          date_default_timezone_set(ProjectConfiguration::DEFAULT_TIMEZONE);
          $time = strtotime($value);

          date_default_timezone_set($timezone);
          $value = date('Y-m-d H:i:s', $time);
        }
      }
    }

    return $value;
  }

  protected function _set($fieldName, $value, $load = true)
  {
    $column = $this->getTable()->getColumnDefinition($fieldName);
    if ($column && 'timestamp' == $column['type'] && $time = strtotime($value))
    {
      $timezone = date_default_timezone_get();
      if (ProjectConfiguration::DEFAULT_TIMEZONE != $timezone)
      {
        // shift value to the default timezone
        date_default_timezone_set(ProjectConfiguration::DEFAULT_TIMEZONE);
        $value = date('Y-m-d H:i:s', $time);

        date_default_timezone_set($timezone);
      }
    }

    return parent::_set($fieldName, $value, $load);
  }
}

To get sfDoctrinePlugin to use this class instead of the default, sfDoctrineRecord, add the following to your ProjectConfiguration.

// config/ProjectConfiguration.class.php
class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
    // ...

    sfConfig::set('doctrine_model_builder_options', array(
      'baseClassName' => 'myRecord',
    ));
  }

  // ...
}

Jun 1

Two ZendCon proposals

I submitted two 400-character proposals to ZendCon yesterday.

Someone else’s symfony

Taking on development of a symfony application started by another developer can be a daunting task, especially for those not familiar with the framework. This will be a practical exploration of symfony from the perspective of a developer taking on an existing project. Topics will include getting started, automated testing, the anatomy of a symfony request, refactoring and symfony best practices.

I think this is a really juicy topic I’ve never seen covered. The current symfony documentation is geared toward either someone new to symfony starting a new project or someone experienced with symfony working on an existing project. I think the population of devs new to symfony on an existing project could use some luv. This also seems like a new and refreshing way to introduce symfony.

The Community Stack

A vital ingredient to any open source project is a vibrant community. Drawing from his experience as Community Manager for the symfony framework, Kris will examine the makeup of a successful “community stack” and propose three basic layers: devs/users, the larger OSS community, and the society as a whole. How these layers interact and grow will be the focus of this session.

The idea here is to acknowledge the importance of community in an open source project with the same significance you would any other solution stack you depend on. I’ve been working on some meat for each of those three layers, but haven’t had a chance to give this talk yet. Here’s hoping that will change!


May 26

Symfony in Concert gets a little attention

I’m happy to report symfony in concert got a little attention yesterday. I sat down with a designer here at Sensio and discussed the forthcoming microsite: something clean that can be built quickly. I think it’s a challenging design because the site is targeted toward two disparate audiences: developers and social organizations. Preparations for symfony live have taken the front seat around here, so I’m just happy to have gotten any time. I’d like something to hang my hat on as Community Manager… I wait on the edge of my seat!

The home page will include a Twitter search feed of the #symfony-concert tag. This is the tag to use if you’re tweeting about the competition.


May 19

Waiting for the Metro, thinking about Forms

I’m below Paris waiting for the 13 and thought I might take advantage of this time to post here…

Things are good! My first days at Sensio have gone well, although I’ve been bailing around lunchtime to catch up on sleep. Fabien and I have been working on that-which-cannot-be-revealed and discussing bits of symfony, components and the like.

I recently introduced the sfFormSymfony class in the 1.3 branch, which includes this pearl of a method:

$form->bindRequest($request);

So clean, so simple. Compare that to the code necessary to do the same thing in the 1.2 branch:

$form->bind(
  $request->getParameter($form->getName()),
  $request->getFiles($form->getName())
);

Ugly, huh? Fabien’s term was “not ugly, explicit” (I couldn’t help but laugh at his turn of phrase).

We dicussed this point for awhile, and there actually some decent arguments against this enhancement. One, surprisingly, is simplicity. This is one of those terms that can mean a number of different things, depending on who you talk to. For me, the new bindRequest() method is a clinic in simplicity.

This simplicity has its limits however. For example, if I were new to symfony and was told to use this method when binding forms in my action layer, I would be very happy. It’s so easy! I may even blog about symfony and tell everyone to use it… But what would I do when it came time to unit test my form? There is no request object, so how do I bind a request to my form now? I don’t know. I guess I won’t unit test my form…

This is a big concern. It’s hard enough to get developers to write automated tests, this runs the risk of only making that harder.

There are other considerations, but those are more easily addressed with enhancements currently in the hopper. So, the jury is still out. I will continue to add this method to my 1.2 projects, but its inclusion in 1.3 is far from assured.


May 15

May 14

Day 3? 4? Maybe 2?

The kids were up early again this morning. I think it was 3-something when I glanced at my watch. Recovering from jet lag is definitely a multi-day ordeal.

Once this half of the world wakes up I’ll be heading into Sensio and hopefully resolving our mobile phone concerns. I think we’ll all feel a bit more comfortable venturing out into the city once we have phones, especially when the kids are in tow.

I’ve got some fun symfony work lined up with Fabien, but it’s probably prudent to be a bit stealth right now. I will say this will be very good for the community — growth type stuff.