Reputation: 1016
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!
Upvotes: 5
Views: 18933
Reputation: 1601
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()
];
},
));
Upvotes: 3
Reputation: 193
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.
Upvotes: 1
Reputation: 193
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 %}
Upvotes: 0
Reputation: 591
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,
));
Upvotes: 3
Reputation: 1016
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.
Upvotes: 14