Reputation: 3622
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?
Upvotes: 3
Views: 1839
Reputation: 3622
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.
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.
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.
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.
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:
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
.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?
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!
Upvotes: 3
Reputation: 406
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
Upvotes: 0
Reputation: 5612
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
Upvotes: 0
Reputation: 44851
If you're using the form type with a model object, set the default on the object itself.
Upvotes: 0