Format a CSV string in Twig

If you’ve ever had to format a string of values in most languages then you’re certainly familiar with the ugly logical hoops you have to jump through just to get the formatting to look good.

Here’s a nice way to do it in Twig:

So given a dirty array of phone numbers:

['123-4567', '', '321-7654']

the above snippet will strip empty values, chomp extraneous whitespace, and output:

Phones: 123-4567, 321-7654

Validating multiple files in Symfony 2.5

Symfony 2.5 implemented handy multiple file upload support, e.g. input fields that look like this:

<input id="form_resumes" multiple="multiple" name="form[documents][]" required="required" type="file" />

Unfortunately there seem to be some problems in the Validator component. For example, it’s difficult to require at least one file. Likewise the All constraint doesn’t play nicely with validation groups. Here are a few workarounds.

To require at least one file but not more than three, use the Count constraint in combination with All and NotNull:

This works around a bug where 'min'=>1 is ignored.

If you’re using validation groups, you may notice that the All constraint is not being applied. Here’s how to fix it:

Note line 11: 'groups' => 'Test',

Technically we shouldn’t have to specify validation groups for the All constraint; in fact there seems to be no way to do this in YAML. Hacking it in as an option to All on the PHP side however seems to be working for me. YMMV. And as far as I can tell this last one is fixed in 2.6.

Know your CSRF timeouts

Web development frameworks such as Symfony these days bake-in a number of security features, CSRF protection being a fairly common one. I recently made the mistake of not scrutinizing Symfony’s CSRF protection defaults, which led to occasional confusing CSRF errors after a web app had been deployed.

CSRF protection embeds a code into both the generated form and some client-side storage mechanism (cookies, though I suppose this could work with an HTML5 client-side database or whatnot). On submit, if the value embedded into the form and the cookie value match, then we know the form is submitted by the user who originally requested it.

In Symfony, the CSRF cookie lifetime can be configured, defaulting to null, which falls back to session.cookie_lifetime value from php.ini. I had originally set this to be one hour, probably based on configuration examples from a year or so back..

Anyway, app users lately have been leaving forms open for more than an hour. This is a good thing: It means they are treating our web app more like a client side app, which indeed it is meant to replace. Took some sleuthing, however, to figure out why I was seeing the occasional CSRF error when everything else seemed hunky dory. I’ve now set this to

which will retain the cookie for the length of the browser session.
Lesson learned. Know your timeouts. And for that matter, plan some time to review old assumptions.

JMS\Serializer\Exception\RuntimeException: “Resources are not supported in serialized data…” Yadda yadda

Been bit by this a couple of times now. It’s really a problem with a bad route. In my case I had some ajax using the FOSJsRoutingBundle to generate a route along the lines of:

Routing.generate('some_titles', {id: $(this).val()});

where val() was actually returning an empty string. The resulting URL (“some//titles”) was invalid, looks to JMSSerializer like a resource thanks to the double slash, and badda bing badda boom: completely irrelevant error.

I think this may also be the source of “Cannot redeclare class Doctrine\ORM\Mapping\Annotation” errors to boot. Double whammy.

Simple Twig Fallback

Love the simplicity of Twig; mainly that it allows us to do away with the conditionals that tend to litter most interface code.  You know the drill: If this then show that otherwise show the other thing except in such-and-such a case..  Confusing and brittle.  Twig’s hierarchical layout is the way to go.

Falling back to a parent block depending on complex output can be tricky however.  Here’s a simple way to do it without a bunch of ifs:

Yay. If less.

Or with one ternary conditional if running parent() through default() is unclear:

{{ details|trim|raw ?: parent() }}

Defeating mysterious “This form should not contain extra fields” errors in Symfony Forms

I love the Symfony framework, but really hate the form component at times. Two years after the initial release of Symfony 2 and we’re still waiting on some decent form validation debugging.

But enough griping. If you’ve ever worked with form events, then undoubtedly you have run into the This form should not contain extra fields error. Basically what it means is that the request parameters don’t match up with the form against which you’re trying to validate.

“Yes, but.. but.. which parameters??” I hear you ask.  Try this:

 

Those “extra data” parameters.

 

Not Defined and Not Empty/Null in Twig

Long time no write.  Came across some syntax today that solves an old annoyance in Symfony's Twig.

Rather than blather on in code with the following complex statement to make sure that a value is both defined and not null:

{% if var is defined and var is not null %}

one can instead simply do this:

{% if var|default is not empty %}

or even more simply:

{% if var|default %}

Nice.

Fixing the Dubious Symfony Test Harness

Although Symfony’s original baked-in test framework, Lime, is being replaced by PHPUnit in Symfony 2, I figure I should post a fix for those, like me, who will be stuck for the foreseeable future in Symfony 1.x with an older suite of tests.

In general, Lime works pretty well except when running multiple tests in the harness. For example, when you run a specific test or tests:

$ ./symfony test-unit util
1..14
# isEmailValid()
ok 1 - returns an integer
ok 2 - test@test.com is a valid email address
ok 3 - test@test is not a valid email address
...

Lime plays along nicely. But when you run all of your tests via the test harness like so:

$ ./symfony test-unit
happyTest......................................................dubious
Test returned status 1
sillyTest......................................................dubious
Test returned status 1
...

the results can be dubious.

“Dubious” here means that the test crashed somehow during execution and so Lime doesn’t know if it would have passed or failed. To see what’s going on, pop open symfony/vendor/lime/lime.php. Depending on your version, you should see a block like the following inside lime_harness->run():

ob_start(array($this, 'process_test_output'));
passthru(sprintf('%s -d html_errors=off -d open_basedir= -q "%s" 2>&1', $this->php_cli, $file), $return);
ob_end_clean();

Never mind that the above passthu may not work on Windows (see ticket #5437), if you comment-out the output buffering and run your tests again, you’re likely to discover that Symfony can’t autoload Symfony libraries, base classes, or what-have-you; for example:

$ ./symfony test-unit
Fatal error: Class 'sfCore' not found in D:\app\test\unit\happyTest.php on line 8
Call Stack:
0.0004 70248 1. {main}() D:\app\test\unit\happyTest.php:0

The easiest fix is to work around this whole executing tests via passthru nonsense. A good old “include”, like Pake does when you run one test at-a-time, will do just fine. Patch lime.php:

ob_start(array($this, 'process_test_output'));
$return = include($file);
ob_end_clean();

Then add a “return 0;” to the end of each of your tests. (Yes, the Lime harness expects zero to indicate success..)

Now the harness should run just fine:

$ ./symfony test-unit
happyTest......................................................ok
sillyTest......................................................ok
All tests successful.
Files=2, Tests=10

Of course, this will break the harness for the functional tests, but that’s a fix for another post.

Appending fixtures via the propel-load-data task in Symfony 1.0.x

Not sure if this is a little more clear in later releases of Symfony, but in 1.0.x the load-data task, as outlined in the documentation and cookbook, is somewhat vague.

If you’re appending a fixture to an existing database, here’s what you want to do:

$ php symfony propel-load-data <APPLICATION_NAME> [<ENVIRONMENT_NAME>] <FIXTURES_DIR_OR_FILE> append

so usually I do something like this:

$ php symfony propel-load-data frontend data/fixtures/import_data.yml append

I’m actually not sure how ENVIRONMENT_NAME would come in to play.. Either this is a way to select per-environment fixture data (probably), or per-environment databases to which to apply the data.

Defeating weird “No database selected [Native Error: MySQL server has gone away]” error when moving code into a partial in Symfony

So you’re working in an older version of Symfony (or maybe even a newer one..), trying to refactor some common code into a partial. Maybe something simple like this from within a global partial?:

include_partial('status');

And suddenly connections to the database start falling down with errors along the lines of:

SQLException: No database selected [Native Error: MySQL server has gone away]

In reality this is a problem with Symfony’s error reporting from within global partials. Do you actually mean?:

include_partial('global/status');

Remember that even if you’re in a global partial, Symfony doesn’t really know that’s where you are.