Skip to content
Dec 8 11

First steps into conference speaking

by rich

I’ve been thinking about next year’s New Year’s Resolutions again. One which I’m going to repeat from this year was to blog more (inspired by Lorna’s post about blogging more back in January), but also a new one I want to achieve is to speak at a conference.

I’ve presented papers at a couple of non-web related conferences a number of years ago, but nothing in the web field, and I’m unsure as to the correct approach to take with this. My main reason for wanting to speak is that I want to be able to share knowledge I’ve learnt in the hope that this may be of interest to at least one person. I’m also passionate about the technology I use in day-to-day development and would most probably like to include this. My main concern however is “would it be interesting enough?”, which I’m inclined to feel is a catch-22 situation – I won’t know until presenting it, but then I won’t get a chance to present it unless I submit.

Calls for papers are continuously springing up, and I’m wondering about the best approach here to get my foot in the door so to speak. Is it a case of writing up something that I feel is interesting and would benefit others, and just taking the leap? Finding local Uncons/user groups and trying the material out there?

I’d be interested to hear any tips and pointers :-)

Dec 5 11

Symfony2 data collectors

by rich

NB: Cross-posted on our work blog at White October :-)

I spent some time last week working on automated MailChimp integration with one of our Symfony2 sites. The project uses Behat and Mink extensively for BDD testing, so I wanted to be able to test that when users registered on our site, they were automatically added to the client’s MailChimp distribution list. Unsubscription could happen via a checkbox in the user’s account settings and this needed to work the same way behind the scenes.

I opted to use one of the available MailChimp bundles to aid integration and with a bit of tweaking this worked great. The bundle comes with a stub connection which is able to collate requests sent and make them available for inspection. I wanted to be able to utilise this neatly inside a set of tests, and potentially also make it available in the dev environment. A bit of skimming the Symfony2 cookbook led me to the entry on Symfony2 data collectors.

The concept is simple – a data collector is registered and is able to, well, collect data! For example, the number of queries during a request, or the number of emails sent. This can then be accessed via the profiler in the relevant environment (dev, test etc). In my case I wanted to collect and subsequently inspect all requests sent to MailChimp.

First step – create me a data collector:

< ?php
 
namespace Jirafe\Bundle\MailChimpBundle\DataCollector;
 
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Jirafe\Bundle\MailChimpBundle\Connection\ConnectionInterface;
 
class MailChimpDataCollector extends DataCollector
{
    private $connection;
 
    public function __construct(ConnectionInterface $connection)
    {
        $this->connection = $connection;
    }
 
    public function collect(Request $request, Response $response, \Exception $exception = null)
    {
        $this->data = array(
            'requests'  => $this->connection->getRequests(),
        );
    }
 
    public function getRequestCount()
    {
        return count($this->data['requests']);
    }
 
    public function getRequests()
    {
        return $this->data['requests'];
    }
 
    public function getName()
    {
        return 'mail_chimp';
    }
}

The collect() method is the interesting part here – this is called whenever data collection is required – usually once per request. We collect the requests from the connection supplied to the constructor.

Next step, register this during the bundle’s load() method:

// Add connection to data collector
$definition = $container->getDefinition('mail_chimp.data_collector');
$definition->addArgument(new Reference(sprintf('mail_chimp.connection.%s', $config['connection'])));

The above uses the connection type specified in the bundle’s configuration option and previously registered as a service in the same method. If you’re creating your own custom collector, this is just the normal parameters you’d pass in to your service when it’s instantiated.

Finally, I added a method and subsequent implementation to the interface and related connection classes within the bundle, so that MailChimp request objects were collected in an array, and made available to the data collector upon request. This is the getRequests() method listed above in the data collector; called in the collect() method. Quick ‘n’ dirty here, since there would only ever be a single MailChimp request (possibly 2) made in any one request.

Once all this was in place, I could then test correctly. Note that I’d rather have done the other way round (test and then develop) but since this was experimentation, I figured it was OK ;-) In my Behat scenarios, I added the getSymfonyProfiler() method detailed in a Behat/Mink cookbook entry, and proceeded to write my step (Behat 1.x-style):

$steps->Then('/^my email address is unsubscribed from the relevant MailChimp list$/', function($world) {
 
    $profiler = $world->getSymfonyProfiler();
    if (!$profiler)
    {
      throw new \RuntimeException("Symfony2 profiler not present for tests, wrong driver?");
    }
    $mcDataCollector = $profiler->getCollector("mail_chimp");
    $listID = $world->container->getParameter("mailchimp_list_id");
 
    // Check the requests made to make sure we have an unsubscribe
    assertEquals(1, $mcDataCollector->getRequestCount());
 
    $requests = $mcDataCollector->getRequests();
    $req1 = $requests[0];
    assertEquals("listUnsubscribe", $req1->getMethod());
 
    $params = $req1->getParams();
    assertEquals($listID, $params["id"]);
 
    // Fire the redirect on the client
    $world->getSession()->getDriver()->getClient()->followRedirect();
});

The final line here is as a result of disabling redirects in a previous step since the data collector operates on a per-request basis. If you’re subscribing eg via a form POST, then you’ll need to disable redirects prior to submitting the form, otherwise your data collector will be empty when you check it. Disable them and then redirect after you’ve carried out your checks.

The implementation for the above is up on our Github MailChimpBundle fork here; I’m not particularly fond of having to hack about the connection classes, and ideally it would be nice to layer the data collector on top without having to adjust existing code. That said, it does the job fine here, and it’s good to use even more features of Symfony2 ;-)

Sep 20 11

Twig extensions – name them correctly!

by rich

Came across this one today whilst integrating some new code into my current Symfony2 project. The project has 2 custom Twig extensions – one with some filters in, one with some custom methods. When creating the latter, I’d copied the class from the former and tweaked to suit. In doing so, I’d left these lines in:

    /**
     * Name of this extension
     *
     * @return string
     */
    public function getName()
    {
        return "myfirstextension";
    }

The Twig documentation states that all extensions should be named uniquely, unless you need to replace any existing extensions, in which case you should name them the same. See here for a better explanation :-) Great for extensibility and adjusting extensions that maybe you don’t have control over. Not so good when both extensions do something different and you don’t quite understand why it’s not working….

Anyway, quick question in the #symfony IRC channel and Stof came to the rescue. Give that man a medal :-) and name your extensions uniquely if you don’t want them to overwrite others!