Custom Field: configureOptions() & Allowing Empty Input

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Thanks to our data transformer - specifically the fact that it throws a TransformationFailedException when a bad email is entered - our UserSelectTextType has some built-in sanity validation!

But, the message we passed to the exception is not what's shown to the user. That's just internal. To control the message, well, we already know the answer! Add an invalid_message option when we create the field.

configureOptions(): Defining Field Options / Default

Or... instead of configuring that option when we're adding the specific field, we can give this option a default value for our custom field type. Open UserSelectTextType, go back to the Code -> Generate menu, or Command + N on a Mac, and this time, override configureOptions(). Inside, add $resolver->setDefaults() and give the invalid_message option a different default: "User not found".

... lines 1 - 11
class UserSelectTextType extends AbstractType
{
... lines 14 - 30
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'invalid_message' => 'Hmm, user not found!',
]);
}
}

Try that out! Go back, refresh and... very nice!

And hey! We've seen this configureOptions() method before inside our normal form classes! When you're building an entire form, configureOptions() is used to set some options on your... whole form. There aren't very many common things to configure at that level.

But when you're creating a custom field type: configureOptions() is used to set the options for that specific field. We've just changed the default value for the invalid_message option. The cool thing is that this can still be overridden if we want: we could add an invalid_message option to the author field and it would win!

Fixing Empty Value Case in the Data Transformer

I want to talk more about field options because they can unlock some serious possibilities. But first, there is a teenie, tiny bug with our data transformer. Clear out the author text box and try to submit. Duh - disable HTML5 validation by adding the novalidate attribute. Hit update!

Oh! Our sanity validation still fails: User not found. That's not quite what we want. Instead of failing, our data transformer should probably just return null.

Go back to EmailToUserTransformer. In reverseTransform(), if $value is empty, just return. So, if the field is submitted empty, null should be passed to setAuthor().

But, hmm... the problem now is that, while it's technically ok to call setAuthor() with a null argument, we want that field to be required!

Re-submit the form! Oof - an integrity constraint violation: it's trying to save to the database with null set as the author_id column. We purposely made this required in the database and this is a great example of... messing up! We forget to add an important piece of business validation: to make the author required. No worries: open the Article class, find the $author field and, above it, add @Assert\NotNull(). Give it a message: Please set an author.

... lines 1 - 17
class Article
{
... lines 20 - 71
/**
... lines 73 - 74
* @Assert\NotNull(message="Please set an author")
*/
private $author;
... lines 78 - 269
}

Try that again. Excellent! This is the behavior - and error - we expect.

Next: how could we make our custom field type behave differently if it was used in different forms? Like, what if in one form, we want the user to be able to enter any user's email address but in another form we only want to allow the user to enter the email address of an admin user. Let's learn more about the power of form field options.

Leave a comment!

  • 2020-01-02 Victor Bocharsky

    Hey Coder,

    Thank you for your feedback! We are happy to hear it's clear and easy to understand :)

    Cheers!

  • 2020-01-01 Coder

    Very good and clear videos about those custom types.

  • 2019-11-11 Victor Bocharsky

    Hey Yuri,

    Yeah, I see your point, but that's the one of many things that makes Symfony Forms so powerful. With this implementation your form behaves as a nested tree, and thanks to this you can make more complex and nested forms, as you can easily put one form into another form, inside another form and so on :) Yeah, definitely, the more practice - the better understanding. Also, go with Symfony Docs about the Form component will helps to understand it better and cover blind spots.

    Cheers!

  • 2019-11-10 Yuri Plashenkov

    Hi Victor, thank you for your response. Separation is great, no problem with that. But I was confused that the form and the form element are represented by the identical class. Intuitively the whole form and just the single element are very different "entities". Well, maybe I should use it in practice for some time to get the idea.

  • 2019-11-06 Victor Bocharsky

    Hey Yuri,

    You could write that code in the same (AtriclyType) form class, but separate that logic allows you to reuse that UserSelectTextType in other forms, and just makes code clearer: now it's 2 small classes instead of one big that holds all the mixed logic inside. But yeah, I see your point. It's simple when you have simple tasks, but complicates and require more work when your tasks become more complex. But this approach is flexible and allows you to build really complex forms that might be nested to each other and where some parts could be re-used in other forms.

    Cheers!

  • 2019-11-06 Yuri Plashenkov

    Hi! When we make a new form, we create some *FormType class which derives from the AbstractType class. And when we want to customise some field we also need to extend AbstractType. This looks like a mess (for now). A whole form vs just a single field. What's the logic behind this?