Symfony 4 Forms: Build, Render & Conquer!

4:45:40

What you'll be learning

Yep, forms! One of the most powerful and... confusing features in all of Symfony. Here's the truth: forms are just plain hard. You need to manage the HTML form elements, validation, data transformation and a lot more. The Form component might be the most complex part of Symfony. But the more you work with it, the more you'll like it.

So, let's work with some forms! We'll learn how to use Symfony forms to handle both simple & complex/ugly situations. Most importantly, I'll show you how to avoid the pitalls that many developers often fall into that causes the form component to spiral into complexity hell. The form system is a tool: in this tutorial, we'll put the joy back into it:

  • Creating a basic form
  • Basic form rendering and customization
  • Handling a form submit
  • Backing your form with an entity
  • Adding validation
  • Filling with default data
  • Creating a form "type" class
  • Understanding how forms really work
  • Flash messages

... and bad jokes + form tips & tricks!


Your Guides
Ryan Weaver

Buy Access
Login or register to track your progress!

Questions? Conversation?

  • 2019-11-01 Victor Bocharsky

    Hey Anton!

    Thank you for this idea, we will add it to our ideas pool for the future screencasts.

    Cheers!

  • 2019-10-31 Anton Pool

    Hey guys,
    This is becomes really important to cover Google's Recaptcha. Thank you in advance

  • 2019-10-18 Irina Serdiuk

    Hey Ryan weaverryan ,
    you are the best! It finally works, yeeey!
    Thanks a lot for this solution.
    Irina.

  • 2019-06-26 weaverryan

    Hey Ben haupt290!

    Awesome conversation here - thanks for adding to it. Just to clear things up, yes, in my original solution, I mean $field->getForm() not $builder->getForm() - sorry about that! I've updated my comment to be accurate.

    Cheers!

  • 2019-06-26 Ben

    Yeah, me too, just a note as I have read here in the thread answers just of not event assigning and no explanation of it.

  • 2019-06-26 Ben

    Hi Alex,
    yeah, I know that form fields in sf extend root form (type) but they still can have default value of the same (inherited) property set to another (inversed to opposite in case of boolean) if it is desirable (which I would understand so from docs quotation "For all fields, this option should only be true for root forms" - so property default value on form fields just doesn't follow it?!).

    I also yesterday have checked the docs source that you mention - quotation comes from it. But inherited properties are also stated on the pages of particular form fields so why not auto_initialize? (Especially when it should be used by developer in any situation.)

    But these are just the comments to Symfony (and its documentation) itself, we wan't resolve it here.

    Thanks for your help and assurance of way of field adding to form - this makes much more sense for me (but first I really tried strict code from weaverryan's answer just ended up with blank screen) and after other testing it seems to be even functional.

  • 2019-06-25 haupt290

    Hi Ben.
    It is true what you said. Thats why I have removed this in my code already. I am adding POST_SUBMIT event listener to second field as sugested by weaverryan above. And it is working fine for me.

    Alex

  • 2019-06-25 haupt290

    Hi Ben.
    FormType is a parent of all other form types. They just extend it. The information is here: https://symfony.com/doc/3.4...
    So each field should have auto_initialize option and default is true.

    This is the correct way to use it: $form->add($field->getForm()); I think that was just misprint.

    Alex

  • 2019-06-25 Ben

    Hi Alex,
    thanks for your answer. However, the option "auto_initialize" isn't mentioned for form fields in Symfony documentation (nor in the docs for version 3.4), except for the form itself (FormType) with default value of true, which corresponds with exception message as I have understood it originally.

    Quotation from docs:type: boolean, default: true
    An internal option: sets whether the form should be initialized automatically.
    For all fields, this option should only be true for root forms. You won't need to change this option and probably won't need to worry about it.

    But I tried your advice and it really helped. Nevertheless it is strange and irritating for me (not for the first time in sf generally) that form fields have "internal" option which I should set in any "specific" situation and which isn't documented though. I would expect also that default value of this option on form fields should be set "correctly" to false (unlike on form itself), to follow statement of "this option should only be true for root forms" from official documentation quotation above.

    Nevertheless, thank you for your usable hint, I will test farther.

    PS: Have you used $form->add($builder->getForm()); (which isn't yet clear for me and which produced blank pages for me) or $form->add($field->getForm()); (as me), Just still don't know what difference (in impact) is between these and what is eventually better solution.

  • 2019-06-24 haupt290

    Hi Ben.
    Just Add option "auto_initialize" = false to your field to solve "Automatic initialization is only supported on root forms. You should set the "auto_initialize" option to false on the field .."
    ChoiceType or EntityType should not make any difference.

    Alex.

  • 2019-06-22 Ben

    Hi Alex, thanks for notification, I checked full thread of question and replied there.

  • 2019-06-22 Ben

    Hi, attempting to implement this hopeful resolution I ended up with blank screen - no HTML nor any other output. I know that it usually indicates something extra "rotten" in coding but can't find it. Blank screen appears just in continuity to implementing this code part.

    Events PRE_SET_DATA are apparently functional as initializing of two dependent fields (that I've separated to "setup..." methods called by these events) needed for initial form displaying (before editing and submitting) is performed - if setup methods use $form->add for new fields creation and don't add any event listeners POST_SUBMIT. Submit of edited form with changed values of first two or all three fields so wouldn't pass of course.

    But as soon as in setup method of middle field $form->add is replaced with $builder->create (builder passed as argument to the method) and form acquired from builder added to $form with the last row of your resolution (so even without event listener adding), blank screen occurs. Understandably, without this last row and so not assigning created new field by builder to current form, dynamically generated/initialized field isn't assigned to the current form at all and page displaying ended up with template error "Neither the property .. nor one of the methods .. exist and have public access in class..." which refers to the first of two dependent fields (all three fields are always displayed and never removed from form in my case, independently on values or no selected choice of previous fields, so they aren't conditional in template - they wouldn't be displayed at all in opposite case).

    So, finally I located cause of blank screen just to this last row, especially:$builder->getForm()It results to blank screen even dump this.

    Personally, I don't understand much, what we want actually add to the form - all the form from builder? I tried also this which would appear for me at first sight more understandable:$form->add($field->getForm());But that resulted to runtime exception: "Automatic initialization is only supported on root forms. You should set the "auto_initialize" option to false on the field .." (my middle field).

    Some differences in my project from code situation designed here in thread by Alex:
    1) I use Symfony 3.4.0
    2) My three dropdowns aren't pure ChoiceType, but EntityType loaded from database using 'query_builder' option for dependency limitation by value of previous field (entity values parameters type taken into consideration, else I would expect similiar manipulation)
    3) As already told, they are displayed always all three inputs (none used to be removed from the form, they are just "empty", with only default null choice, if previous dropdown hasn't selected value)

    Maybe something of these points is the reason for blank screen.

  • 2019-06-21 Ben

    Hi, I'm not a Symfony expert anyway but suppose to be clear from quoted code that conditional block beginning "if ($builder->has('specificLocationName'))" on the end of buildForm method (adding EventListener POST_SUBMIT on 'specificLocationName' field) will never be executed and so this event neither assigned because 'specificLocationName' field is initialized in PRE_SET_DATA event (method setupSpecificLocationNameField) which occurs later, after form creating using buildForm method (else events either couldnť be defined/assigned here in this method). And elsewhere this event isn't assigned. So this block has in the code no sense/impact and event meant to reload third dropdown, second dependent (specificLocationName2), on form submit is left unassigned and unused.

  • 2019-06-20 Diego Aguiar

    This is the answer haupt290 is talking about: https://symfonycasts.com/sc...

  • 2019-06-20 haupt290

    Hi @Ben.
    Look the previous question from Alex. By mistake I asked two the same questions. weaverryan found the wounderful solution.

  • 2019-06-20 haupt290

    Hi weaverryan .
    What can I say!!! You are the star.
    It worked like a charm. It is possible now to add any number of fields with this set up.
    I am really appreciate your help.

    Thank you very much.

    Alex

  • 2019-06-20 weaverryan

    Hey Alex!

    Ah, dang! Let's see if we can figure out how to work around this! Here's our next attempt :)

    1) Pass the $builder as an argument to setupSpecificOccupationNameField() - you have access to it both places that you call this method. So, it will become:


    private function setupSpecificOccupationNameField(FormInterface $form, ?string $category, FormBuilderInterface $builder) {

    2) Use the $builder to create the new form:


    // ...

    // your existing code, unchanged


    $field = $builder->create( 'occupation', ChoiceType::class, [
    'mapped' => true,
    'required' => false,
    'choices' => $choices,
    'placeholder' => 'Select Occupation',
    ] );
    $field->addEventListener(
    FormEvents::POST_SUBMIT,
    function (FormEvent $event) { /* ... your real code */ {
    );
    $form->add($field->getForm());

    Let me know if that works - it may not be exact - I'm eyeing the source code and navigating through it. But this should do it, or be very close :).

    Cheers!

  • 2019-06-20 Ben

    Personally bypassed that issue using manual resetting properties of underlaying model data object that is then passed to the form, in controller action processing post request, before creating and initializing of the form, with new values from request. Meaning just fields (their values) that affect others. But that is very "unclean" resolution and hoped to find here instructions to solve it correctly and using appropriate Symfony's resources.

  • 2019-06-19 haupt290

    Hi weaverryan .
    Thank you for your help. But I think I tried this already and I am getting BadMethodCallException "Unmodifiable event dispatchers must not be modified".
    Is any other way to by-pass this? What about event subscriber?
    Thank you

    Alex

  • 2019-06-19 weaverryan

    Hi Alex!

    Yes, I agree that you've found the problem: you add the event listener to occupation... but then remove and re-add this field later and you do *not* re-attach the listener. Probably the listener code for occupation should be attached inside of setupSpecificOccupationNameField(). I believe you can do it like this:


    // ...

    // your existing code, unchanged
    $form->add ( 'occupation', ChoiceType::class, [
    'mapped' => true,
    'required' => false,
    'choices' => $choices,
    'placeholder' => 'Select Occupation',
    ] );
    $form->get('occupation')->getConfig()-> getEventDispatcher()->addListener(
    FormEvents::POST_SUBMIT,
    function (FormEvent $event) { /* ... your real code */ {
    );

    That's a bit complex... basically... that is what's happening under the surface normally when you use the addEventListener() method in the other location.

    Let me know if this helps!

  • 2019-06-17 haupt290

    Hi weaverryan.
    Thank you for your reply. I was trying to solve this for a month. I understand why we need data transformation. And I can see where it is failing. I just dont know how to work around. I did as you asked and added this line $this->choiceList->getValues() and have got old values for skills field. They were never updated.
    The problem as I see in POST_SUBMIT event listener which is missing from "occupation" field. The POST_SUBMIT event is trigered on "category" field(first field) and fills "occupation" field with new valid choices and then POST_SUBMIT should be triggered on "occupation" field and fill "skills" field with new choices( depends on what user selected in "occupation" field), but second part is never happend because POST_SUBMIT event Listener is never added to "occupation" field. I add "occupation" field and "skills" field in PRE_SET_DATA event on the form. And I dont know how to add POST_SUBMIT event Listener to "occupation" field at this point. I tryed to add "occupation" field in buildForm() and add POST_SUBMIT event listenere to this field there as well but later when PRE_SET_DATA is triggered we remove "occupation" field and I assume the POST_SUBMIT event Listener is removed as well by Symfony. I had debug log statements in method called by POST_SUBMIT on "occupation" field and this was never called. So, I just wonder, how to make sure that POST_SUBMIT event Listener on "occupation" field is always added? Please see code above.

    Thank you

    Alex

  • 2019-06-17 weaverryan

    Hey Alex!

    You can see why these things are so tough! You did a good job finding these latest details. The important thing is:

    > TransformationFailedException: Unable to reverse value for property path "skills": The choice "ACCA" does not exist or is not unique

    This means that the problem is not "real" validation (e.g. NotBlank annotations, etc), but a problem during "data transformation". Imagine that you have only *one* simple drop-down (nothing fancy) with Yes/No options. If a "bad" user edited your HTML and added a third option ("Foo"), *this* is the error that would happen internally (to prevent them from submitting an invalid option).

    So, at some level, the same thing is happening in your situation: the form system sees this "ACCA" value, looks at the "skills" field, looks at the "choices" set on it, and determines that ACCA is not a valid option. The "event listeners" we added are supposed to help with this, by (at the last second) updating the "choices" in the skills field based on the other data (occupation). Something is going wrong with this part. It could be something complex... or it could be as simple as that DropDownFactory::getSkillsList() method needs to swap the keys and values. One thing that might help debugging is this:

    1) Open up this core Symfony file in your project: https://github.com/symfony/...

    2) Right *before* that exception on line 48, try this:


    var_dump($this->choiceList->getValues());

    That might give you an idea of what the valid values for your skills field are at the moment of failure, which might help debugging.

    Let us know what you find out!

    Cheers!

  • 2019-06-12 haupt290

    hi Victor Bocharsky
    May be exception stack is a help to understand what is going on.


    Default Data
    Property Value
    Model Format same as normalized format
    Normalized Format "Other"
    View Format same as normalized format

    Submitted Data
    Property Value
    View Format "ACCA"
    Normalized Format null
    Model Format same as normalized format



    Message Origin Cause
    This value is not valid. skills Caused by:
    ConstraintViolation {#1487 ▼
    root: Form {#1311 …}
    path: "children[skills]"
    value: "ACCA"
    }
    TransformationFailedException {#1389 ▼
    #message: "Unable to reverse value for property path "skills": The choice "ACCA" does not exist or is not unique"
    #code: 0
    #file: "C:\xampp7\htdocs\FindMee\vendor\symfony\form\Form.php"
    #line: 1129
    trace: {▶}
    …1
    }
    TransformationFailedException {#1387 ▼
    #message: "The choice "ACCA" does not exist or is not unique"
    #code: 0
    #file: "C:\xampp7\htdocs\FindMee\vendor\symfony\form\Extension\Core\DataTransformer\ChoiceToValueTransformer.php"
    #line: 48
    trace: {▶}
    }

    Thank you
    Alex

  • 2019-06-12 haupt290

    Hi Victor Bocharsky .
    I don't have any constraint on this field. It is just a string without any length and nullable=true.
    On submit the modified form ( I am only modifying occupation and skills fields) the POST_SUBMIT event is not triggered on occupation field (because it was never added ) to put fresh value to skills field.
    And i am getting red text in form "Invalid data" and constraint violation in debug console.
    In your videos you describe that on submit the PRE_SET_DATA event is removing fields to pass validation and POST_SUBMIT event will fill with new data. I hope I understood correctly.
    Any idea?

    Thank you
    Alex

  • 2019-06-12 Victor Bocharsky

    Hey Alex,

    Wait, you're getting constraint violation? about what? Because you have some constraints on "UserProfile::$occupation" field? It makes sense then I think. I'd recommend you to move those constraints (only those related to UserProfile::$occupation) to the form type instead, you can do something like this:


    $builder
    ->add('yourFieldName', null, [
    'constraints' => [
    new NotBlank(),
    new Length(['min' => 3]),
    // ...
    ],
    ])
    ;

    or see docs: https://symfony.com/doc/cur...

    Btw, are you trying to implement it working with JS? I'd recommend you to implement it on pure PHP and then, when it works - add JS logic for this as we did in this course.

    I hope this helps!

    Cheers!

  • 2019-06-11 haupt290

    Hi Victor Bocharsky
    Thank you for your reply. I have been trying your sugestion since you posted it and just gave up today. I am getting the same ConstraintViolation Exception.
    I am posting code below, so if you are interested you could have a look. I can see in the logger "IN Category POST_SUBMIT" message but I never see "POST_SUBMIT ADDED to OCCUPATION" message (means POST_SUBMIT event never added even if i add 'occupation' field later in setupSpecificOccupationNameField triggered by POST_SUBMIT on 'category'). javascript part is working as fields are loaded with correct data. It is only problem when data was modified and is failing on submit on last field. I have Category, Occupation and Skills fields. It looks like that POST_SUBMIT event listener is never set for Occupation field. And this is real world scenarion, but it looks like it is impossible to do it on the server side. I hope I am wrong and I am doing something stupid.
    Thank you. Looking forward for your reply.
    Alex


    namespace App\Form;

    use App\Entity\Category;
    use App\Entity\Occupation;
    use App\Entity\Skills;
    use App\Entity\UserProfile;
    use App\Form\Model\DropDownFactory;
    use Psr\Log\LoggerInterface;
    use Symfony\Bridge\Doctrine\Form\Type\EntityType;
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\Form\Extension\Core\Type\TextType;
    use Symfony\Component\Form\FormEvent;
    use Symfony\Component\Form\FormEvents;
    use Symfony\Component\Form\FormInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
    use Symfony\Component\Form\Extension\Core\Type\FileType;
    use Symfony\Component\Form\Extension\Core\Type\TextareaType;
    use Symfony\Component\Validator\Constraints\NotNull;

    class UserProfileType extends AbstractType
    {
    /**
    * @var LoggerInterface $logger
    *
    */
    private $logger;

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
    /**
    * @var UserProfile|null $profile
    */

    $this->logger = $options['logger'];

    $builder
    ->add ( 'category', ChoiceType::class, [
    'required' => false,
    'placeholder' => 'Select Category',
    'choices' => DropDownFactory::getCategoryList()
    ] ) ;
    $builder->addEventListener(
    FormEvents::PRE_SET_DATA,
    function (FormEvent $event){
    /**
    * @var UserProfile|null $data
    */
    $data = $event->getData();
    if (!$data) {
    return;
    }
    $this->setupSpecificOccupationNameField(
    $event->getForm(),
    $data->getCategory()
    );
    $this->setupSpecificSkillsNameField(
    $event->getForm(),
    $data->getCategory(),
    $data->getOccupation()
    );
    }
    );

    $builder->get('category')->addEventListener(
    FormEvents::POST_SUBMIT,
    function (FormEvent $event) use ($builder){
    $form = $event->getForm();
    $this->logger->debug('--------------IN Category POST_SUBMIT------------------- ');
    $this->setupSpecificOccupationNameField(
    $form->getParent(),
    $form->getData()
    );
    }
    );
    if($builder->has('occupation')) {
    $this->logger->debug('-------------- POST_SUBMIT ADDED to OCCUPATION------------------- ');
    $builder->get('occupation')->addEventListener(
    FormEvents::POST_SUBMIT,
    function (FormEvent $event) {
    $this->logger->debug('--------------IN OCCUPATION POST_SUBMIT ------------------- ');
    $form = $event->getForm();
    $this->setupSpecificSkillsNameField(
    $form->getParent(),
    'Account management',
    $form->getData()
    );
    }
    );
    }
    }

    private function setupSpecificOccupationNameField(FormInterface $form, ?string $category) {
    if(null === $category) {
    $form->remove('occupation');
    $form->remove('skills');
    return;
    }
    $choices = $this->getOccupations($category);
    if (null === $choices) {
    $form->remove('occupation');
    $form->remove('skills');
    return;
    }
    $form->add ( 'occupation', ChoiceType::class, [
    'mapped' => true,
    'required' => false,
    'choices' => $choices,
    'placeholder' => 'Select Occupation',
    ] );
    }
    private function setupSpecificSkillsNameField(FormInterface $form, ?string $category, ?string $occupation) {
    if(null === $occupation ) {
    $form->remove('skills');
    return;
    }
    $choices = $this->getSkills($category ,$occupation);
    if (null === $choices) {
    $form->remove('skills');
    return;
    }
    $form->add ( 'skills', ChoiceType::class, [
    'required' => false,
    'mapped' => true,
    'label' => 'Select available skills',
    'choices' => $choices,
    'multiple' => false,
    'placeholder' => 'Select Skills',
    ] );
    }
    public function configureOptions(OptionsResolver $resolver)
    {
    $resolver->setDefaults([
    'data_class' => UserProfile::class,
    ]);
    $resolver->setRequired('logger');
    }

    private function getOccupations(?string $category) {
    if ($category !== null) {
    return DropDownFactory::getOccupationList($category);
    }
    return null;
    }
    private function getSkills(?string $category, ?string $occupation) {
    $this->logger->debug('Category '. $category);
    $this->logger->debug('Occupation '. $occupation);
    if($occupation !== null) {
    return DropDownFactory::getSkillsList($category, $occupation);
    }
    return null;
    }
    }

  • 2019-06-06 Victor Bocharsky

    Hey Alex,

    Here's an example I made for you:


    namespace App\Form;

    use App\Entity\Article;
    use App\Entity\User;
    use App\Repository\ArticleRepository;
    use App\Repository\UserRepository;
    use Symfony\Bridge\Doctrine\Form\Type\EntityType;
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
    use Symfony\Component\Form\Extension\Core\Type\TextType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\Form\FormEvent;
    use Symfony\Component\Form\FormEvents;
    use Symfony\Component\Form\FormInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;

    class ArticleFormType extends AbstractType
    {
    private $userRepository;

    public function __construct(UserRepository $userRepository)
    {
    $this->userRepository = $userRepository;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
    /** @var Article|null $article */
    $article = $options['data'] ?? null;
    $isEdit = $article && $article->getId();

    $builder
    ->add('title', TextType::class, [
    'help' => 'Choose something catchy!'
    ])
    ->add('content', null, [
    'rows' => 15
    ])
    ->add('author', UserSelectTextType::class, [
    'disabled' => $isEdit
    ])
    ->add('location', ChoiceType::class, [
    'placeholder' => 'Choose a location',
    'choices' => [
    'The Solar System' => 'solar_system',
    'Near a star' => 'star',
    'Interstellar Space' => 'interstellar_space'
    ],
    'required' => false,
    ])
    ;

    if ($options['include_published_at']) {
    $builder->add('publishedAt', null, [
    'widget' => 'single_text',
    ]);
    }

    $builder->addEventListener(
    FormEvents::PRE_SET_DATA,
    function (FormEvent $event) {
    /** @var Article|null $data */
    $data = $event->getData();
    if (!$data) {
    return;
    }

    $this->setupSpecificLocationNameField(
    $event->getForm(),
    $data->getLocation()
    );

    $this->setupSpecificLocationNameField2(
    $event->getForm(),
    $data->getSpecificLocationName()
    );
    }
    );

    $builder->get('location')->addEventListener(
    FormEvents::POST_SUBMIT,
    function(FormEvent $event) {
    $form = $event->getForm();
    $this->setupSpecificLocationNameField(
    $form->getParent(),
    $form->getData()
    );
    }
    );
    if ($builder->has('specificLocationName')) {
    $builder->get('specificLocationName')->addEventListener(
    FormEvents::POST_SUBMIT,
    function(FormEvent $event) {
    $form = $event->getForm();
    $this->setupSpecificLocationNameField2(
    $form->getParent(),
    $form->getData()
    );
    }
    );
    }
    }

    private function setupSpecificLocationNameField(FormInterface $form, ?string $location)
    {
    if (null === $location) {
    $form->remove('specificLocationName');
    $form->remove('specificLocationName2');

    return;
    }

    $choices = $this->getLocationNameChoices($location);

    if (null === $choices) {
    $form->remove('specificLocationName');
    $form->remove('specificLocationName2');

    return;
    }

    $form->add('specificLocationName', ChoiceType::class, [
    'placeholder' => 'Where exactly?',
    'choices' => $choices,
    'required' => false,
    ]);
    }

    private function setupSpecificLocationNameField2(FormInterface $form, ?string $specificLocation)
    {
    if (null === $specificLocation) {
    $form->remove('specificLocationName2');

    return;
    }

    $choices = $this->getLocationNameChoices2($specificLocation);

    if (null === $choices) {
    $form->remove('specificLocationName2');

    return;
    }

    $form->add('specificLocationName2', ChoiceType::class, [
    'placeholder' => 'Where exactly 2?',
    'choices' => $choices,
    'required' => false,
    ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
    $resolver->setDefaults([
    'data_class' => Article::class,
    'include_published_at' => false,
    ]);
    }

    private function getLocationNameChoices(string $location)
    {
    $planets = [
    'Mercury',
    'Venus',
    'Earth',
    'Mars',
    'Jupiter',
    'Saturn',
    'Uranus',
    'Neptune',
    ];

    $stars = [
    'Polaris',
    'Sirius',
    'Alpha Centauari A',
    'Alpha Centauari B',
    'Betelgeuse',
    'Rigel',
    'Other'
    ];

    $locationNameChoices = [
    'solar_system' => array_combine($planets, $planets),
    'star' => array_combine($stars, $stars),
    'interstellar_space' => null,
    ];

    return $locationNameChoices[$location] ?? null;
    }

    private function getLocationNameChoices2(string $specificLocation)
    {
    $locationNameChoices = [
    'Earth' => [
    'The USA' => 'The USA',
    'Ukraine' => 'Ukraine',
    'Mexico' => 'Mexico',
    'Moldova' => 'Moldova',
    ],
    ];

    return $locationNameChoices[$specificLocation] ?? null;
    }
    }

    You need to add a new field Article::$specificLocationName2 with getter and setter. And render this field in your template like:


    {# ... #}
    {{ form_row(articleForm.location) }}
    {% if articleForm.specificLocationName is defined %}
    {{ form_row(articleForm.specificLocationName) }}
    {% endif %}
    {% if articleForm.specificLocationName2 is defined %}
    {{ form_row(articleForm.specificLocationName2) }}
    {% endif %}
    {# ... #}

    I hope this helps, at least you can start with it. Good luck!

    Cheers!

  • 2019-06-05 haupt290

    Hi Victor Bocharsky
    Thank you for your reply. I have followed your thread. And it works with two fields no problem. I am fighting with this problem for good few weeks and cannot get working. POST_SUBMIT event listener on first and second field should do all the magic. But if you remove field to pass validation and them add field the POST_SUBMIT listener is removed and cannot be added.

    I am adding POST_SUBMIT event listeners to first field and second field and PRE_SET_DATA to the form to update second field and third field. But when press submit button on a form PRE_SET_DATA event is triggered and will remove second field and POST_SUBMIT event listener for second field as well. Then POST_SUBMIT event is triggered on first field and will add second field but adding POST_SUBMIT event listener for second field is too late as Form system started iterating through registered listeners already and this new added will be ignored. So field number 3 will not be updated with new value and throws Violation Exception

    I hope i am wrong and there is simple solution. Thank you.
    Alex

  • 2019-06-05 Victor Bocharsky

    Hey Alex,

    Hm, it's similar to the https://symfonycasts.com/sc... - did you follow that thread?

    Fairly speaking, I've never try this before, but I think 3 dropdowns should work the same! So, you just add one listener for PRE_SET_DATA as we do and in anon function at first call setupSpecificLocationNameField(), and after call another method that depends on specificLocationNameField, where in the last you do all the check base on the specificLocationNameField. Did you try this?

    I hope this helps.

    Cheers!

  • 2019-05-25 haupt290

    Thank you @Diego Aguiar
    But I would like to do it on a server and I am not friendly with any java script frameworks

  • 2019-05-24 Diego Aguiar

    Hey haupt290

    That's advanced ;) To be honest I'm not sure how to actually do it using Symfony Form component, probably you would be better doing it with another tool (Maybe ReactJS?), you can handle all the logic for updating input fields in your frontend, and on submit, you can rely on Symfony Validation component so you don't miss validations

    Cheers!

  • 2019-05-24 haupt290

    Hi. This course is amazing!
    I would like to ask you a question about Dynamic TypeChoice fields. When I have only 2 TypeChoice fields it works no problem. Select one will update second one. But when I want to use 3 TypeChoice fields and each depend on what was selected in previous one It is not working and end up with Violation exception in field 3.
    I am adding POST_SUBMIT event listeners to first field and second field and PRE_SET_DATA to the form to update second field and third field. But when press submit button on a form PRE_SET_DATA event is triggered and will remove second field and POST_SUBMIT event listener for second field as well. Then POST_SUBMIT event is triggered on first field and will add second field but adding POST_SUBMIT event listener for second field is too late as Form system started iterating through registered listeners already and this new added will be ignored. So field number 3 will not be updated with new value and throws Violation Exception.

    How this problem can be sorted?
    Any suggestions are appreciated.
    Thank you
    Alex

  • 2019-05-24 Alex

    Hi. The course is amazing.
    I have a question about Dynamic ChoiceType choices. It works with two dropdowns with no problem. You select element in one and next dropdown will have all choices related to element was selected in dropdown one. But imagine that you have 3 dropdowns and each next related to what you selected before.
    I am trying to add PRE_SUBMIT event listener to 2 previous dropdowns and not to the last. When PRE_SET_DATA event is triggered after pressing submit on a form It will remove the second dropdown and of course the PRE_SUBMIT event listener for this dropdown will be removed as well. When the PRE_SUBMIT will be triggered on the first dropdown and will add second dropdown but adding PRE_SUBMIT at this point to this dropdown number 2 does not make sense as Form system already iterating through the list of registered listeners and will ignore it. So dropdown number 3 will have always Violation Exception.

    How to deal with problem? What be the easiest way?

    Thank you

    Alex

  • 2019-02-11 weaverryan

    Hey toni!

    We're going to start releasing a course on uploading... hopefully next week. Watch for it!

    Cheers!

  • 2019-02-10 toni

    The course is great, but I notice the absence of file uploads, like images.
    I'm having some problems editing forms with images and I can not find anything. Any course that you recommend?

  • 2019-01-21 Victor Bocharsky

    Hey Radu,

    Btw, we're talking about handling password in this course in case you missed it:
    https://symfonycasts.com/sc...

    Maybe it might help you a bit.

    Cheers!

  • 2019-01-17 Diego Aguiar

    Hey Radu Barbu!

    Thanks for your kind words :) but this tutorial (Symfony Forms) is finished now, Anyway, we will consider your request.

    Cheers!

  • 2019-01-16 Radu Barbu

    First of all, this is premium stuff! I love it!
    Hope this series is not over yet cuz I would love to see some casts on editing more complex forms such as user profile form.
    Handling the password and profile picture fields is a delicate task.

  • 2018-12-19 weaverryan

    Hey Lopoi Popoi!

    Hmm, I'm not sure if there are any open source projects with a big example - the closest is probably the "finished" code from the tutorial I linked to. And, boy, if you have a "position", then you're going to want to do re-ordering... and also it sounds like you have file-uploads, which really means that you shouldn't just make the user choose 5 uploads, then process them all at once on submit (like the CollectionType would want) - you should upload them immediately via AJAX. I really think a JavaScript-powered solution is a better bet in this situation!

    Cheers!

  • 2018-12-19 Lopoi Popoi

    Thanks weaverryan !
    And your remark about avoiding this solution also valuable.
    May be you know some opensourced project to see code examples?
    I am struggling with product images , they have position field in product gallery, filename and some other fields as a image meta-data.

  • 2018-12-18 weaverryan

    Hey Lopoi Popoi!

    Already covered :) https://symfonycasts.com/sc...

    That is for Symfony 3, but nothing significant changed with this. The CollectionType is a really complex field type - it's tough to get set up and still sometimes may not work perfectly, depending on your situation. So, we DO have a tutorial on it, but I try to avoid it when I can and instead use a JavaScript/API/AJAX setup to accomplish something like this - it's a better user experience anyways.

    Cheers!

  • 2018-12-17 Lopoi Popoi

    Hello SymfonyCasts team!
    Please do not forget to cover embedded forms collection topic with add/edit option!

  • 2018-11-27 Alex

    weaverryan Thank you very much. This is definitely what I was looking for.

  • 2018-11-23 weaverryan

    Hey @Alex!

    Same answer! S3 - it can scale FOREVER. And, you can even configure your S3 bucket to automatically backup or even version. Heck, you can even configure images in S3 to move to "Glacier" after some period of time (another storage service - cheaper, but you can't get the files out very quickly). I don't know about Facebook specifically, but, for sure, they have some sort of big cloud storage - e.g. S3 - and they store the images/videos there (forever). In other words, Facebook likely uses something very similar to S3 - it scales forever.

    Let me know if that makes sense!

  • 2018-11-20 Alex

    weaverryan Thank you for your answer. But if you have a website where the number of users are increasing every day and they all have gallery of some kind with, may be, hundreds of images. Where would you store all those images then? And you need to back up all that as well.
    I have researched this problem on the web and different people does it differently. For example, where Facebook keeps all the images and videos we post?? Sorry for bothering you again.
    Thank you

  • 2018-11-20 weaverryan

    Hey @Alex!

    Excellent question! We don't deal with a lot of "uploads" here on Symfonycasts, but we do have a few, and we store them all in S3 using Flysystem. Flysystem makes communicating with S3 quite nice, and S3 gives us the ability to have public files or private files (which we can then make "public" temporarily via signed URLs - e.g. like if you want to allow a user to download a file). For large uploads, well, it depends on *how* large. The easiest way to handle uploads is to have the user upload to your server, and then you send it to S3. However, you can also ask S3 for a temporary URL and then have the user upload directly to that URL (basically, eliminating your server as the middleman). That setup is a bit more complex, but scales for larger files.

    Cheers!

  • 2018-11-20 Alex

    Hi Ryan. You are doing a great job. Thank you.
    I am looking for advice: How to store images of a lot of users?
    In Database or File System?
    Usually the users upload large images and in different formats. How to deal with all this?
    Thank you again.
    Media is a big part of any website in our days.
    Alex

  • 2018-10-26 Victor Bocharsky

    Hey there!

    We're working on this tutorial right now, so this will be the next. Thank you for your patience!

    Cheers!

  • 2018-10-26 伟伟权

    Come on! Come on! I'm waiting.

  • 2018-10-23 Victor Bocharsky

    Hey Bob,

    Thank you for your patience! We're working on this tutorial right now, so this one will be the next after "Symfony Security: Beautiful Authentication, Powerful Authorization" tutorial I think.

    Cheers!

  • 2018-10-23 Bob

    Can’t wait until This is availble. Been loving symfony 4 because of symfonycasts🙂

  • 2018-10-08 weaverryan

    Hey Daniele!

    Hmm, great question! Both!? :)

    Well, really, your entity most of the time. Until/unless you have a form that doesn’t really match your entity (e.g. a few different fields). Then, a model class in that case.

    But, some people use model classes all the time. There’s definitely know issue with this - I just think it adds extra work with limited benefit. But ultimately, it’s a matter of taste :).

    Cheers!

  • 2018-10-08 Daniele Grillenzoni

    Is this going to recommend binding entities to forms or use separate objects?

  • 2018-09-14 Hermen

    Thanks for the tip!

  • 2018-09-14 El Tebe

    You should add the EWZRecaptchaBundle (https://github.com/excelweb... to your project,
    then insert a new form element (just like the builtin / service ones) in your form declaration:

    $builder->add('recaptcha', EWZRecaptchaType::class, array(
    'language' => 'en'
    // ...
    ));

    It suppors the "image" and the "invisible" version of Google's Recaptcha too.

  • 2018-09-13 Hermen

    Hey guys and gals,

    Are you going to cover recaptcha to?

  • 2018-09-11 Radu Barbu

    Cool, then I'm going to watch S3 until S4 appears.

    Btw, you're doing a great job with the S4 series so far!

  • 2018-09-10 weaverryan

    Hey Radu Barbu!

    *Probably* after our security tutorial, which just started release yesterday :). I can't promise for sure - I'm still finishing the security tutorial, but this is my next target. Until then, the Symfony 3 version of the tutorial - https://knpuniversity.com/s... - is still quite relevant. The Symfony directory structure, is different, as are some service config things. But, the core of the "form" system actually hasn't changed much - it's all still up to date.

    Cheers!

  • 2018-09-08 Radu Barbu

    Any news on when this course will be launched? Can't wait for it!

  • 2018-08-14 weaverryan

    Hey Peter Kosak!

    1) I see! For this, you should use ChoiceType directly. This may or may not be easy - there's a shortcut I've never tried :). Set the choices option to your dynamic data - e.g. $product->getProductsForDropdown(), where the key of each item in the array would be the display value, and the value of each item in the array would be the Product objects themselves. If this works, then, on submit, everything should just work. I've never used the ChoiceType and set the values of the "choices" to entity objects... but I think it should work. Let me know! :)

    2) Interesting idea about the audit table! Usually, this is done with a Doctrine event listener. However, the downside to that approach is that, for example, if a Product is being updated, that's all you know: you don't know exactly which form in the system is causing the update.

    This is indeed a very interesting, and complex idea - I'll see if it fits into the tutorial! The way to achieve this would be to:

    A) Create a "form type extension" - https://symfony.com/doc/cur... - which you could use to modify every form in the entire system

    B) Inside the buildForm() method of that form type extension, attach an event listener - https://symfony.com/doc/cur... - probably on the SUBMIT or POST_SUBMIT events.

    With this, you would now have a callback that is executed whenever ANY form in your system is submitted, and you could use that to log stuff. This isn't a full explanation of course, you would still need to create an entity, use dependency injection to inject the EntityManager into your form type extension, etc - but hopefully it can get you started!

    Cheers!

  • 2018-08-09 Peter Kosak

    Thanks for reply:
    - 1st: Yes thats what everyone is suggesting but my situation is slighly more complicated. Imagine you have a printed invoice with invoice number. With product "product" but admin can change this to "amazing product" suddenly your invoice is different when you print it before and after admin change when using entity type.
    I could use a flag in product table to deactivate "product" and create a new "amazing product" (so it would have different id") and in dropdown create custom query to list only active products but it would be more easier for me just to save choice value instead of entity. I have tried to change choice_value for entity type in form builder. Saving was working fine but when accessing same invoice dropdown values & options would be populated correctly but selected item in this case "product" would reverted back to "Please choose" so what I would like to achieve is to have ChoiceType populated by values from DB(Entity -> getProductsForDropdown) lets say my array looks like:

    $productChoices = [
    "product" => "product",
    "other amazing product" => "other amazing product"
    ...
    ]

    But I have no idea how to pass it into form type properly.

    -2nd: Lets say you are shopping and you will add "product 1" to cart.
    I would like to log this action into database like
    User: Peter, Product: product 1, Action: Added to Cart, TimeStamp
    When user delete from cart I would like to log into db
    User: Ryan, Product: product 2, Action: Deleted from Cart, TimeStamp
    Then when user submit any form also search form to log it into DB
    User: John, Product: blank, Action: Search for product "amazing product", TimeStamp
    Basically I could on form handling populate this manually like
    <code. $log="new" log();="" $log-="">setUser(blabla);
    $log->setAction(blabla);
    $log->.....;
    $em->persist($log);
    $em->flush();

    but what if you have 40 routes where user can submit a form I would have to repeat this 40 times.

    I would like generic solution that would work with any submit. At least to log action.

    User,Action (Clicked on Submit button) and get submitted data.

    This would be useful for tracibility and also for audit if manager will ask I didnt delete "product" and admin will go to audit table search for product and find out that user John deleted product from cart on 14/8/2018 at 05am :D

    Or listen for event addToCart anywhere in the software.

    I am beginner so hopefully it makes sence.

  • 2018-08-09 weaverryan

    Hey Peter Kosak!

    GREAT suggestions! The items in the link are indeed a pain in the butt ;). Can you tell me a bit more about:

    > binding option values to ChoiceType coming from DB

    Do you mean, you want to create a ChoiceType with options that come from the database? Are you not able to use the EntityType? Or do you have a more complex situation?

    And can you tell me more about this one too?

    > on submit log action with data to audit table

    Thanks for the suggestions! Cheers!

  • 2018-08-08 Peter Kosak

    Please please please include examples of each of these http://symfony.com/doc/curr... & form theming & binding option values to ChoiceType coming from DB & on submit log action with data to audit table would be a bonus for most of us (at least me lol)