programming

A simple example on how to use post validators in symfony

Someone came into the #symfony channel on freenode asking how to add conditional validators that would require a text box to be filled out when a checkbox was ticked. As I realize this is a common question (though the circumstances may change), I wanted to put this example up here as well, for others.

Here’s the example:

class TestForm
{
  public function configure()
  {
    // ... snip ...
 
    $this->validatorSchema->setPostValidator(
      new sfValidatorCallback(array('callback' => array($this, 'contextualChecks')))
    );
  }
 
  public function contextualChecks($validator, $values)
  {
    if ($values['checkbox_1'] == 1)
    {
      if ($values['textbox_for_checkbox_1'] == '')
      {
        $error = new sfValidatorError($validator, 'Field is required when Checkbox 1 is checked.');
        $ves = new sfValidatorErrorSchema($validator, array('textbox_for_checkbox_1' => $error));
        throw $ves;
      }
    }
    return $values;
  }
}

Note: I completely left out returning $values in the initial draft. I apologize!

Jack and the majaxPheanstalk

I have just made my first release to GitHub, majaxPheanstalkPlugin for symfony 1.4.x (possibly will work on older ones). It’s a set of tools that help to access Beanstalkd and make building workers much simpler.

It builds off of the work of Paul Annesley (pda)‘s pheanstalk which provides a simple php library for accessing a Beanstalkd server.

The goal is to provide you with simple tools for managing your Beanstalkd integration. Right now it’s just worker thread tools and a simple factory, but I hope to grow it into a more complete library as time goes on.

Taking base_convert past base 36

When building my URL shortener, ZapX, I ran into a bit of a problem. I wanted to be able to make the shortest possible urls using the characters 0-9, a-z, and A-Z, otherwise known as base 62.

PHP’s native base_convert only goes up to base 36, so I was forced to resort to my own devices to get the job done. Long story short, here’s one that goes up to base 62:

function extended_base_convert($dec, $base)
{
 $numchart = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
 $min_base = 2;
 $max_base = strlen($numchart);
 if ($base < $min_base || $base > $max_base)
  return 'N/A';
 $value = $dec;
 $ptr = ceil($value / $base);
 $buf = array();
 do {
  $buf[$ptr] = substr($numchart, ($value % $base), 1);
  $value = intval($value / $base);
  $ptr--;
 } while ($value > 0);
 $buf = array_reverse($buf);
 return implode('', $buf);
}

Invalidating old cache content on dynamic data in Symfony

Here’s just a quick note for those of you working with caching solutions for dynamic data in Symfony.

Update: If testability is a concern, please read the follow up to this article.

One of the challenges with caching is deciding where the limit is. What’s worth caching, and what isn’t. Because cleaning up that old data is hard… isn’t it?

Not so much!

You just need to make a helper and a checks, and you’ll be expertly picking out the templates to invalidate!

The first hurdle with clearing out invalid data, is that usually your backend is separate from the frontend, so it’s a non-trivial reach to try and invalidate cache files of other applications. Or is it?

<?php
 
class cacheAssistant
{
  public static function clearCachePattern($pattern)
  {
    $envs = array('prod', 'dev');
    $apps = array('backend', 'frontend');
    foreach($envs as $env)
    {
      foreach($apps as $app)
      {
        $app_cache_dir = sfConfig::get('sf_cache_dir').DIRECTORY_SEPARATOR.$app. DIRECTORY_SEPARATOR.$env.DIRECTORY_SEPARATOR.'template';
 	$cache_vars = array(
          'cache_dir' => $app_cache_dir,
   	  'cache_key_use_vary_headers' => true,
          'cache_key_use_host_name' => true,
        );
        $cache = new sfFileCache($cache_vars);
        $cache->removePattern($pattern);
      }
    }
  }
}

Your $cache_vars will differ depending on your cache settings, but essentially, that’s your main helper setup.

Now here’s what I use in my objects for cache clear detection:

<?php
 
class MyObject extends Doctrine_Record
{
  private $pendingClearCacheEntries = false;
  public function preSave($event)
  {
    if ($this->isModified())
      $this->pendingClearCacheEntries = true;
  }
  public function postSave($event)
  {
    if ($this->pendingClearCacheEntries)
    {
      $this->clearCacheEntries();
      $this->pendingClearCacheEntries = false;
    }
  }
  public function clearCacheEntries()
  {
    cacheAssistant::clearCachePattern('**/**/pages/index');
  }
}

Of course, your patterns will change depending on your cache options (I.e. I use the **/** because I have enabled cache_key_use_host_name, other settings may not accept this pattern.)

Happy caching, and if you have any questions, feel free to let me know!

Using JavaScript Responsibly

Since we’ve started the conversation, let’s take a step back and talk about something a little less fun, but just as important: when is it okay to require JavaScript use?

My hard and fast rule is that anywhere a login is required to reach, you can require JavaScript. If it’s publicly accessible, however, you should do everything possible to avoid requiring JavaScript. It takes more work to have a JavaScript and non-JavaScript version, but it makes sure your site remains accessible.

twitter