简体   繁体   中英

Additional properties to entity Field Type in a form in Symfony2

In Symfony2, is there a way to map more fields from an entity to the option tag of a select dropdown generated from a form which is based on an entity?

I currently have something like:

    $builder->add('creditcard', 'entity',
        array( 'label' => 'Credit Card',
            'required' => true,
            'expanded' => false,
            'class' => 'Acme\Bundle\Entity\CreditCard',
            'property' => 'display_text',
            'multiple' => false,
            'query_builder' => function(\Acme\Bundle\Repository\CreditCardRepository $er)  {
                return $er->createQueryBuilder('b');
            },
            'mapped' => false,
        ));

This works just fine, but I would like to generate something like:

<option value="id" string_mapped_from_field1="value_of_field1">display_text</option>

Thanks!

Ok, in case somebody gets here with the same question, this is what I've done in the end:

I've created a custom field type (see http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html )

Since we is going to be an entity field in the end, you want to add:

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

When using it on the Form:

    $builder->add('creditcard', new CreditCardFieldType(),
        array( 'label' => 'Credit Card',
            'required' => true,
            'expanded' => false,
            'class' => 'Acme\Bundle\Entity\CreditCardCharge',
            'property' => 'object',
            'multiple' => false,
            'query_builder' => function(\Acme\Bundle\Repository\CreditCardChargeRepository $er)  {
                return $er->createQueryBuilder('b');
            },
            'mapped' => false,
        ));

object is a new property added to the entity that contains the whole object, so I added to the entity:

public function getObject()
{
    return $this;
}

This way we can access to the object from the template, we just need to create a new template for our own custom field type:

{% block creditcard_widget %}
    {% spaceless %}
        {% if required and empty_value is none and not empty_value_in_choices %}
            {% set required = false %}
        {% endif %}
        <select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
            {% if empty_value is not none %}
                <option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ empty_value|trans({}, translation_domain) }}</option>
            {% endif %}
            {% if preferred_choices|length > 0 %}
                {% set options = preferred_choices %}
                {{ block('choice_creditcard_widget_options') }}
                {% if choices|length > 0 and separator is not none %}
                    <option disabled="disabled">{{ separator }}</option>
                {% endif %}
            {% endif %}
            {% set options = choices %}
            {{ block('choice_creditcard_widget_options') }}
        </select>
    {% endspaceless %}
{% endblock creditcard_widget %}

{% block choice_creditcard_widget_options %}
    {% spaceless %}
        {% for group_label, choice in options %}
            {% if choice is iterable %}
                <optgroup label="{{ group_label|trans({}, translation_domain) }}">
                    {% set options = choice %}
                    {{ block('choice_creditcard_widget_options') }}
                </optgroup>
            {% else %}
                <option value="{{ choice.data.creditcard }}" charge="{{  choice.data.charge }}" {% if choice is selectedchoice(data.creditcard_charges_id) %} selected="selected"{% endif %}>{{ choice.data.text|trans({}, translation_domain) }}</option>
            {% endif %}
        {% endfor %}
    {% endspaceless %}
{% endblock choice_creditcard_widget_options %}

And register it for twig in your config.yml:

twig:
    form:
        resources:
            - 'AcmeBundle:Form:creditcardfield.html.twig'

Not sure it is the best solution but it does the trick. Hope it helps.

an other solution is to declare a function __toString() on CreditCardCharge entity

http://symfony.com/doc/current/reference/forms/types/entity.html#property

like :

public function __toString(){
   return $this->data1.'-'.$this->data2;
}

this function must return a string, replace my sample by your logic

and remove the "property" option to use this function by default

$builder->add('creditcard', 'entity',
        array( 'label' => 'Credit Card',
            'required' => true,
            'expanded' => false,
            'class' => 'Acme\Bundle\Entity\CreditCard',
            'multiple' => false,
            'query_builder' => function(\Acme\Bundle\Repository\CreditCardRepository $er)  {
                return $er->createQueryBuilder('b');
            },
            'mapped' => false,
        ));

since Symfony 2.7 you can use choice_attr

$builder->add('creditcard', 'entity',
    array( 'label' => 'Credit Card',

        // ...

       'choice_attr' => function($object, $key, $index) {
           return [
               'string_mapped_from_field1' => $object->getValueOfField1()
           ];
       },

    ));

I fixed it by putting this in my empField.php :

{%- block choice_widget_options -%}
    {% for group_label, choice in options %}
        {%- if choice is iterable -%}
            <optgroup label="{{ group_label|trans({}, translation_domain) }}">
                {% set options = choice %}
                {{- block('choice_widget_options') -}}
            </optgroup>
        {%- else -%}
            <option value="{{ choice.value }}" data-position="{{ choice.data.position }}" data-avatar="{% path choice.data.avatar, 'reference' %}"{% if choice is selectedchoice(value) %} selected="selected"{% endif %}>{{ choice.data.fullname }}</option>
        {%- endif -%}
    {% endfor %}
{%- endblock choice_widget_options -%}

(it is from : \\vendor\\symfony\\symfony\\src\\Symfony\\Bridge\\Twig\\Resources\\views\\Form\\form_div_layout.html.twig) And it works, I'm not sure it was the right thing to do but yeah it works.

I tried to use this exemple with my entity, but i have an issue. It doesn't detect if a value is selected after the page load and it doesn't persist after submit.

I feel it is related to this line :

<option value="{{ choice.data.creditcard }}" charge="{{  choice.data.charge }}" {% if choice is selectedchoice(data.creditcard_charges_id) %} selected="selected"{% endif %}>{{ choice.data.text|trans({}, translation_domain) }}</option>

I am not sure to understand what to put instead of : data.creditcard_charges_id .

This is how looks mine :

{% block emp_widget %}
    {% spaceless %}
        {% if required and empty_value is none and not empty_value_in_choices %}
            {% set required = false %}
        {% endif %}
        <select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
            {% if empty_value is not none %}
                <option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ empty_value|trans({}, translation_domain) }}</option>
            {% endif %}
            {% if preferred_choices|length > 0 %}
                {% set options = preferred_choices %}
                {{ block('choice_emp_widget_options') }}
                {% if choices|length > 0 and separator is not none %}
                    <option disabled="disabled">{{ separator }}</option>
                {% endif %}
            {% endif %}
            {% set options = choices %}
            {{ block('choice_emp_widget_options') }}
        </select>
    {% endspaceless %}
{% endblock emp_widget %}

{% block choice_emp_widget_options %}
    {% spaceless %}
        {% for group_label, choice in options %}
            {% if choice is iterable %}
                <optgroup label="{{ group_label|trans({}, translation_domain) }}">
                    {% set options = choice %}
                    {{ block('choice_emp_widget_options') }}
                </optgroup>
            {% else %}
                <option value="{{ choice.data.id }}" data-position="{{ choice.data.position }}" data-avatar="{% path choice.data.avatar, 'reference' %}" {% if choice is selectedchoice(choice.data.id) %} selected="selected"{% endif %}>{{ choice.data.firstname }} {{ choice.data.lastname }}</option>
            {% endif %}
        {% endfor %}
    {% endspaceless %}
{% endblock choice_emp_widget_options %}

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