简体   繁体   中英

Symfony2 entity with default value and embedded forms

I have a class which is meant to be a form type (and, in turn, embedded into another form) for selecting commune, region and country, each with a dropdown list (region depends on country). I want a certain country to be selected by default when constructing the country list. This is what I have:

$factory = $builder->getFormFactory();
$builder->add('pais', 'entity', array(
'class' => 'Codesin\ColegiosBundle\Localidad\Pais',
'property' => 'nombre',
'query_builder' => function (EntityRepository $repositorio) {
    $qb = $repositorio->createQueryBuilder('pais')
        ->orderBy('pais.nombre');
        return $qb;
    }
));

I've tried using preferred_value (it says I need an object, which I can't fetch from anywhere given I've got no database access inside the class) and data (which doesn't work). I tried using the id of the entity for both cases. How do I set a selected default value, in this case?

Alright, I think I finally understood how to make it as a service. "Why would you make it a service?", you may ask? Well, because I need to inject the entity manager to this. I hope the following tutorial is of any use.

Preferred_choices in custom form types

Let's say you have several countries saved in your database, and they are a Country entity, properly defined in Doctrine as such. But there's a problem - there are nearly 250 countries around the world, and the user is most likely to be from one single country! So, let's say, you need a country to be selected by default, or to be at the top of the list.

Step 1: Populate the field

Let's say we're building a custom form type, CountryType . Let's define it.

class CountryType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('country', 'entity', array(
             'class' => 'Acme\AcmeBundle\Locality\Country',
             'property' => 'name',
             'query_builder' => function (EntityRepository $repository) {
                 $qb = $repository->createQueryBuilder('country')->orderBy('country.name');
                 return $qb;
             }
        ));
    }

    public function getDefaultOptions(array $options)
    {
        return array('data_class' => 'Acme\AcmeBundle\Locality\Country');
    }

    public function getName()
    {
        return 'countrytype';
    }

}

And when you call it from somewhere, as in,

$builder->add('country', new CountryType());

It all works good, except you don't get a selected country by default. Worse, if you try to add

'preferred_choices' => array(15) //Say, 15 is the id of the country

to your options, you will get an exception. And for some weird reason, doing this:

$defaultCountry = new Country();
$defaultCountry->setId(15);

and then

'preferred_choices' => array($defaultCountry)

does not work either.

Here's where fun begins.

Step 2: Add an EntityManager

In order to search for the entity we need, we will have to query the database. And right now we cannot access it as we don't have a Doctrine EntityManager . What we will do is to add one to our class, passing it to the constructor as an argument and storing it as a private property of the class.

class CountryType extends AbstractType
{
    //Add this
    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('country', 'entity', array(
             'class' => 'Acme\AcmeBundle\Locality\Country',
             'property' => 'name',
             'query_builder' => function (EntityRepository $repository) {
                 $qb = $repository->createQueryBuilder('country')->orderBy('country.name');
                 return $qb;
             }
        ));
    }

    public function getDefaultOptions(array $options)
    {
        return array('data_class' => 'Acme\AcmeBundle\Locality\Country');
    }

    public function getName()
    {
        return 'countrytype';
    }

    //Add this
    private $em;
}

We query the database afterwards.

class CountryType extends AbstractType
{
    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        //Add the following lines
        $qb = $this->em->createQueryBuilder();
        $resultset = $qb->select('country')
                        ->from('Acme\AcmeBundle\Locality\Country', 'country')
                        ->where('country.id = ?1')
                        ->setParameter(1, 15)
                        ->getQuery()
                        ->execute();

        $default = $resultset[0];

        $builder->add('country', 'entity', array(
             'class' => 'Acme\AcmeBundle\Locality\Country',
             'property' => 'name',
             'preferred_choices' => array($default), //Add this too
             'query_builder' => function (EntityRepository $repository) {
                 $qb = $repository->createQueryBuilder('country')->orderBy('country.name');
                 return $qb;
             }
        ));
    }

    public function getDefaultOptions(array $options)
    {
        return array('data_class' => 'Acme\AcmeBundle\Locality\Country');
    }

    public function getName()
    {
        return 'countrytype';
    }

    private $em;
}

But now we run into a problem: we cannot call it anymore!! How do we pass it an EntityManager , especially if we're not calling this from a controller but rather we're embedding it? Here's where magic is to be done.

Step 3: Make it a service

Why? Because this way we can inject an EntityManager to it. Go to Acme/AcmeBundle/Resources/cofig/services.yml and add the following lines:

acme.acmebundle.countrytype:
    class: Acme\AcmeBundle\Types\CountryType
    arguments: ["@doctrine.orm.entity_manager"]
    tags:
        - { name: form.type, alias: countrytype }

Some notes here:

  • The argument passed to the service is another service, namely, a Doctrine EntityManager . This argument is passed to the function's constructor and injected from somewhere else. This way we will make sure we always have an EntityManager .
  • In the tags, make sure to include both tags named. The alias tag must match whatever is returned from getName() in your custom type, in this case, countrytype .

If you want to know more about dependency injection and services, go here: Service Container.

Now, the big question: how do we call it?

Step 4: Calling the service

It's extremely easy to do. Remember this line?

$builder->add('country', new CountryType());

Now change it to the following:

$builder->add('country', 'countrytype');

That's it! Now your custom type has an entity manager attached to it, and no arguments had to be passed. Good luck in your next project!

如果您将表单类型与模型对象一起使用,请在对象本身上设置默认值。

Try at SET_DATA or POST_SET_DATA form events: You could do something like this inside the lambda function:

$data = $event->getData();
$form = $event->getForm();

// search in the form for the right entity to set as country.. let's consider $country

$data->setCountry($country);
$event->setData($data);

It may work. Try and let us know on the progress

you need to insert this ready bundle https://github.com/shtumi/ShtumiUsefulBundle

and configure it here https://github.com/shtumi/ShtumiUsefulBundle/blob/master/Resources/doc/dependent_filtered_entity.rst

i use the framework to make dependent forms

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM