Last week, I was working on a Symfony2 app where I wanted to generate optgroup tags inside the select box of an Entity form type. After poking around, I ran across a StackOverflow answer explaining how to do it. Basically, it turns out what you have to do is manually return a “choices” array from a class that has access to the Entity Manager. I ended up adding a method to my custom repository and passing that repository into my form:
<?php
// My custom repository class
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityRepository;
class CategoryRepository extends EntityRepository {
public function getCategoryOptions($studyId){
$ret = array();
$categories = $this->getEntityManager()
->getRepository("CTBEBundle:Category")
->getQuery()
->getResult();
foreach( $categories as $cat ){
if( !$cat->getParentCategory() ){
continue;
}
if(!array_key_exists($cat->getParentCategory()->getName(), $ret) ){
$ret[ $cat->getParentCategory()->getName() ] = array();
}
$ret[ $cat->getParentCategory()->getName() ][ $cat->getId() ] = $cat;
}
return $ret;
}
}
<?php
// My form type
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class SelectCategoryType extends AbstractType
{
private $formOptions;
public function __construct($options = array()){
$this->formOptions = $options;
}
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add("category", "entity", array("empty_value" => "Please select...", "required" => false,
"class" => 'CTBEBundle:Category',
"choices" => $this->formOptions["em"]->getReceiptCategoryOptions(),
));
}
public function getName(){
return 'selectCategory';
}
}
// And then in the controller, we're creating the types with:
$this->createForm(new ReceiptItemType(array("em" => $this->getDoctrine()->getRepository("CTBEBundle:Category")), $object);
It’s a bit messy and I’m surprised there isn’t an option on the Entity Type to pass in a callback with access to the Entity Manager to generate a choice list. Looking at the source of DoctrineType it looks like you could potentially create a custom type to extend the Entity type and then access the em from your custom function. Even that though, seems like overkill to accomplish something that is reasonably common.