8

I am looking to populate a choice box in symfony2 with values from a custom query. I have tried to simplify as much as possible.

Controller

class PageController extends Controller
{

    public function indexAction()
    {
      $fields = $this->get('fields');
      $countries =  $fields->getCountries(); // returns a array of countries e.g. array('UK', 'France', 'etc')
      $routeSetup = new RouteSetup(); // this is the entity
      $routeSetup->setCountries($countries); // sets the array of countries

      $chooseRouteForm = $this->createForm(new ChooseRouteForm(), $routeSetup);


      return $this->render('ExampleBundle:Page:index.html.twig', array(
        'form' => $chooseRouteForm->createView()
      ));

    }
}

ChooseRouteForm

class ChooseRouteForm extends AbstractType
{

  public function buildForm(FormBuilderInterface $builder, array $options)
  {

    // errors... ideally I want this to fetch the items from the $routeSetup object 
    $builder->add('countries', 'choice', array(
      'choices' => $this->routeSetup->getCountries()
    ));

  }

  public function getName()
  {
    return 'choose_route';
  }
}
0

3 Answers 3

19

You could pass the choices to your form using..

$chooseRouteForm = $this->createForm(new ChooseRouteForm($routeSetup), $routeSetup);

Then in your form..

private $countries;

public function __construct(RouteSetup $routeSetup)
{
    $this->countries = $routeSetup->getCountries();
}

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('countries', 'choice', array(
        'choices' => $this->countries,
    ));
}

Updated (and improved) for 2.8+

Firstly you don't really need to pass in the countries as part of the route object unless they are going to be stored in the DB.

If storing the available countries in the DB then you can use an event listener. If not (or if you don't want to use a listener) you can add the countries in the options area.

Using Options

In the controller..

$chooseRouteForm = $this->createForm(
    ChooseRouteForm::class,
    // Or the full class name if using < php 5.5
    $routeSetup,
    array('countries' => $fields->getCountries())
);

And in your form..

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('countries', 'choice', array(
        'choices' => $options['countries'],
    ));
}

public function configureOptions(OptionsResolver $resolver)
{
    $resolver
        ->setDefault('countries', null)
        ->setRequired('countries')
        ->setAllowedTypes('countries', array('array'))
    ;
}

Using A Listener (If the countries array is available in the model)

In the controller..

$chooseRouteForm = $this->createForm(
    ChooseRouteForm::class,
    // Or the full class name if using < php 5.5
    $routeSetup
);

And in your form..

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
            $form = $event->getForm();
            /** @var RouteSetup $routeSetup */
            $routeSetup = $event->getData();

            if (null === $routeSetup) {
                throw new \Exception('RouteSetup must be injected into form');
            }

            $form
                ->add('countries', 'choice', array(
                    'choices' => $routeSetup->getCountries(),
                ))
            ;
        })
    ;
}
Sign up to request clarification or add additional context in comments.

7 Comments

So, as of Symfony 2.8, it is deprecated to pass in an instantiated form class. Now you need to pass the fully qualified class name instead. Which takes away from being able to use the constructor... That sucks, because I don't want to have to load the options from the controller as in my case, this choice field is used from MANY controllers and would make sense to construct the choices in the form class, however, how do you get the doctrine manager into the form class without using the options or services...? hack hack hack, unless im missing something, please say so
To be honest this wasn't the best approach even then. I'll update.
No worries, your post was 2013 and 2.8 is like a few months ago. Thanks for the update... Ill get to playing around with it now
If you actually need help then I would ask a question and I can stick an answer in there rather than adding a confusing response in the comments or in the given answer.
No thanks, I seem to be getting there now. Appreciate it though, and sorry for digging up the old post :)
|
11

I can't comment or downvote yet, so I'll just reply to Qoop's answer here: What you proposed will work unless you start using your form type class as a service. You should generally avoid adding data to your form type object through constructor.

Think of form type class like a Class - it's a kind of description of your form. When you make an instance of form (by building it) you get the Object of form that is build by the description in form type and then filled with data.

Take a look at this: http://www.youtube.com/watch?v=JAX13g5orwo - this situation is described around 31 minute of the presentaion.

You should use form event FormEvents::PRE_SET_DATA and manipulate fields when the form is injected with data. See: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#customizing-your-form-based-on-the-underlying-data

2 Comments

What's the advantage of this? Why not just check if Form Data $routesetup is set? And if not don't add the field / throw a nice error?
This was the best practice approach, I just corrected the code in the question to make it work rather than "improve" it at. I've updated my answer now with the `PRE_SET_DATA approach mentioned and another (due to a new comment, as opposed to me being a crazy that re-reads my answer from 2+ years ago).
0

I got it working by calling getData on the builder

FormBuilderInterface $builder

// Controller

$myCountries = $this->myRepository->all(['continent' => 'Africa']);
$form = $this->createForm(CountriesType::class, $myCountries);

//FormType

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('pages', ChoiceType::class, [
                'choices' => $builder->getData()
            ])
        ;
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.