Simplicity vs Power: Symfony Forms

Disclaimer: This article covers using Symfony Framework without the Doctrine ORM

As a follow-up on a conversation on Twitter, I decided to compile our dev team’s experience in using Symfony Forms for almost three years in production.

Coming from a CodeIgniter background, we’ve found Symfony’s Form and Security component to be the most powerful and most complex parts of the framework.

Domain & Type – Duplication

Splitting the form generation/handling into two parts was one of the biggest changes for us. We followed the Symfony guidelines and avoided polluting our controllers with $this->createFormBuilder() but since we don’t use Doctrine entities, every form required us to make a dedicated domain object and a dedicated type object.

This led to us to duplicate a lot of form-related stuff. Symfony has very powerful validation and it’s a pity it has to be split across form domains and their corresponding types.
For example, you can @Assert\Choice for a specific field, but you still have to repeat those choices when adding a ChoiceType; you can @Assert\Type("integer") a field, but you still have to mark it as an IntegerType.
Now, I understand, splitting the form information from form representation is a good thing in theory, but in reality, it was more of a chore than a boost.

Our specific business case required us to  build a lot of forms (133), so duplication hit us hard.
Following the official cookbook article to reduce duplication didn’t work for us, so we ended up not using inherit_data. Eventually, using the plain OO inheritance proved better.

Form Modifiers

If you have to implement multi-step AJAX forms or have some kind of dependent AJAX filters in your form, chances are you’ll get an advice to use them.

Don’t.
We’ve found them so complicated (sometimes even impossible) to setup, test and debug that, if we had another opportunity, would skip using Symfony Forms in AJAX-heavy scenarios altogether.

Debugging Form Validation

Now, this may be because of the first issue – when validation is split between the domain and the type, it’s hard to find out which one caused it.
Or, this could be because of the modular nature of form building (every type inside a form type can have it’s own child type and so on), so it’s not so easy to flatten those errors and find out where and why it occurred.

Either way, every time a form isValid returns false, we know we’re in for a serious head-scratching.

To Conclude..

I didn’t mean for this to be a rant on Symfony Form component’s complexity.
It is a very sophisticated and thoroughly tested library we’d use on a project any day. It has helped us numerous times and we’re always discovering new ways in optimizing using it (it has changed a lot since we’ve started using it in Symfony 2.2).
The point I’m trying to get across is, while powerful, this library has a steep learning curve, a lot of gotchas and one needs really to master it in order to use it effectively, beyond the simplest use cases.

Handling MongoDB Dates in PHP (New Driver)

In case you’re enthusiastic like me and wanted to use the new MongoDB driver for PHP ASAP, you’ve might have stumbled upon differences in date handling.

Untill now, when you’ve wanted to properly insert dates, you’d construct new MongoDate and insert it as such. Well, there is no such class any more in the new driver, and to insert a date properly, you need to use new MongoDB\BSON\UTCDatetime. Notice the difference in constructor arguments, though – the new UTCDateTime accepts Unix timestamp in milliseconds, not seconds like MongoDate did.

Of course, you have the use the new class when querying as well:

$docs = $collection->find([
 'ts' => [
  '$gt' =>new MongoDB\BSON\UTCDatetime($unix_timestamp * 1000)
  ]
 ]);

And when you fetch your objects, you can use toDateTime to get native PHP’s DateTime and format it as you’d like.