skip to Main Content

I had this working previously but it stopped working with Symfony 2.7

What I want is to render an expanded/multiple entity choice list such that I display multiple custom properties. The goal is to list the choices as:

{name} – {description} More info

So I created a custom form type with “entity” as parent so I could customize the form rendering

<?php
namespace StudyMainBundleFormType;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentFormFormView;
use SymfonyComponentFormFormInterface;
use SymfonyComponentOptionsResolverOptionsResolver;

class ScholarshipEntityType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->setAttribute('dataType', $options['dataType']);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'required' => false,
            'dataType' => 'entity'
        ));
    }

    public function getAllowedOptionValues(array $options)
    {
        return array('required' => array(false));
    }

    public function getParent()
    {
        return 'entity';
    }

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

}

I render the type as follows (it was just based off of the Twitter Bootstrap bundle template):

{% block scholarship_entity_widget %}
{% spaceless %}
    {% if expanded %}
        {% set label_attr = label_attr|merge({'class': (label_attr.class|default(''))}) %}
        {% set label_attr = label_attr|merge({'class': (label_attr.class ~ ' ' ~ (widget_type != '' ? (multiple ? 'checkbox' : 'radio') ~ '-' ~ widget_type : ''))}) %}
        {% if expanded %}
            {% set attr = attr|merge({'class': attr.class|default(horizontal_input_wrapper_class)}) %}
        {% endif %}
        {% for child in form %}
            {% if widget_type != 'inline' %}
            <div class="{{ multiple ? 'checkbox' : 'radio' }}">
            {% endif %}
                <label{% for attrname, attrvalue in label_attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}>
                    {{ form_widget(child, {'horizontal_label_class': horizontal_label_class, 'horizontal_input_wrapper_class': horizontal_input_wrapper_class, 'attr': {'class': attr.widget_class|default('')}}) }}
                    {{ child.vars.label.name|trans({}, translation_domain) }}
                    - {{ child.vars.label.description }}
                    <a href="{{ child.vars.label.link }}" target="_blank">More Information</a>
                </label>
            {% if widget_type != 'inline' %}
            </div>
            {% endif %}
        {% endfor %}
        {{ block('form_message') }}
        {% if expanded %}
        {% endif %}
    {% else %}
        {# not being used, just default #}
        {{ block('choice_widget_collapsed') }}
    {% endif %}
{% endspaceless %}
{% endblock %}

Finally, I use my custom type in another form:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        // ...
        ->add('scholarships', new ScholarshipEntityType(), array(
            'class' => 'StudyMainBundle:Scholarship',
            'query_builder' => function(EntityRepository $er) use ($options) {
                return $er->findAllByOfferingQueryBuilder($options['offering']);
            },
            'choice_label' => 'entity',
            'multiple' => true,
            'expanded' => true,
            'label' => 'financial.scholarships'
        ))
    ;
}

The “property” I’m rendering is just the entity itself:

/**
 * Scholarship
 *
 * @ORMTable(name="scholarship")
 * @ORMEntity(repositoryClass="StudyMainBundleRepositoryScholarshipRepository")
 */
class Scholarship
{
    /**
     * @var integer
     *
     * @ORMColumn(name="id", type="integer")
     * @ORMId
     * @ORMGeneratedValue(strategy="AUTO")
     */
    private $id;

    // ...

    /**
     * Get the Entity object for form rendering
     * 
     * @return StudyMainBundleEntityScholarship
     */
    public function getEntity()
    {
        return $this;
    }
}

Unfortunately, it looks like my trick which was passing the entire Entity to Twig and letting me access properties is no longer working. There is some change where the label is rendered as a string (I changed ‘property’ to ‘choice_label’ above for 2.7, if that matters).

Error:

Catchable Fatal Error: Object of class StudyMainBundleEntityScholarship could not be converted to string

Stack Trace:

1. in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php at line 251   + 
2. at ErrorHandler ->handleError ('4096', 'Object of class StudyMainBundleEntityScholarship could not be converted to string', '/var/project/vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php', '251', array('choice' => object(Scholarship), 'key' => '0', 'label' => object(Closure), 'values' => array('2'), 'index' => array('SymfonyBridgeDoctrineFormTypeDoctrineType', 'createChoiceName'), 'attr' => null, 'isPreferred' => array(), 'preferredViews' => array(), 'otherViews' => array(), 'value' => '2', 'nextIndex' => '2')) 
in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php at line 251   + 
3. at DefaultChoiceListFactory ::addChoiceView (object(Scholarship), '0', object(Closure), array('2'), array('SymfonyBridgeDoctrineFormTypeDoctrineType', 'createChoiceName'), null, array(), array(), array()) 
in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php at line 185

Is there another way to achieve this?

I was thinking about the following (but don’t know exactly how to do these or if it’s worth looking into any of them):

  • transformers
  • custom type that derives from Choice and does what I want (maybe from a bundle)
  • using the choice list factory somehow
  • passing the entity as some additional field instead of the label (maybe the new ‘choice_attr’?)

2

Answers


  1. If I understood correctly the problem, you should implement the __toString() function in your entity, that will format the string you want to print in the Choice list for you entity.

    For example:

    function __toString() {
      return sprintf("%s - %s", $this->type, $this->description);
    }
    
    Login or Signup to reply.
  2. Try to use the method AbstractType::buildView(FormView, FormInterface, array). There you can access the variables that get passed to the template.

    I used it for a DaterangeType to declare separate ids and names for two date fields:

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars['full_name_0'] = $view->vars['full_name'] . '[0]';
        $view->vars['full_name_1'] = $view->vars['full_name'] . '[1]';
    
        $view->vars['id_0'] = $view->vars['id'] . '_0';
        $view->vars['id_1'] = $view->vars['id'] . '_1';
    }
    

    You can then access these values as standard twig variables.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search