I have a Symfony form that display a list of patients with a checkbox to select each one. I'm trying to format the display of the choice_label sent to the form to make it more readable.
I've tried several PHP functions and HTML tags in the return statement to try and add formatting or even spaces but was unsuccessful.
->add('sampleExtractions', EntityType::class, [
'class' => SampleExtraction::class,
'multiple' => true, 'expanded' => true,
'choices' => $this->sampleExtractionRepository->getDNAExtractions(), 'by_reference' => false,
'choice_label'=> function(SampleExtraction $sampleExtraction) {
$patientName = $sampleExtraction->getPatientSample()->getPatient()->getLastNameFirst();
$patientMRN = $sampleExtraction->getPatientSample()->getPatient()->getMRN();
$patientDOB = $sampleExtraction->getPatientSample()->getPatient()->getDateOfBirth();
return 'Name: ' . $patientName . 'MRN: ' . $patientMRN . $patientDOB->format('Y-m-d'); }
])
As you can see I've tried adding details to the return but it all runs together.
I would like to be able to format the output to make it more readable for the end user.
For this you have to create (or update) your form theme, check How to Work with Form Themes > Fragment Naming for Individual Fields. Check also a good example in Symfony demo application.
Here for example
{% block _YOUR_FORM_TYPE_NAME_ sample_extractions_widget %}
{# your custom HTML here #}
{% endblock %}
Related
Suppose we have Symfony form type and a row adding field
$builder->add('name', 'text', ['attr' => ['class' => 'firstName', placeholder => 'first name']]);
I want this to be merged with attributes set in twig template:
{{ form_row(form.name, {'attr':{'class':'newClass'}}) }}
Currently it does replace. What is the best way to solve it?
You can use form variables to get form class attr, then concatene another class attr:
{{ form_row(form.name, {'attr':{'class':'newClass ' ~ form.name.vars.attr.class|default('')}}) }}
EDIT:
Corrected my previous answer after reading https://symfony.com/doc/current/reference/forms/twig_reference.html#reference-form-twig-variables
I tried to follow this answer to handle nested collections in forms.
I have an Application's Form with a collection of LienAppliServ's Form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('servLiens', 'collection', array(
'label' => ' ',
'type' => new LienAppliServType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' =>false,
'prototype' => true,
))
//...
In my LienAppliServ's Form, I have another collection of PortLienAppliServ's Form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('ports', 'collection', array(
'type' => new PortLienAppliServType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'by_reference' =>false
))
//...
And the form of PortLienAppliServ is :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('numPort')
->add('type')
;
}
Now, I'd like to handle add/delete for each collection...
As I said, I tried to follow this answer
In order to do that, I tried :
{% block body -%}
{{ form_start(form) }}
<ul id="col-servliens" data-prototype="{{ form_widget(form.servLiens.vars.prototype)|e }}">
{# iterate over each existing tag and render its only field: name #}
{% for servLiens in form.servLiens %}
<li>{{ form_row(servLiens) }} </li>
<ul id="col-ports" data-prototype="{{ form_widget(ports.vars.prototype)|e }}">
{%for ports in servLiens.ports %}
<li>{{ form_row(ports) }}</li>
{% endfor %}
{% endfor %}
</ul>
{{ form_end(form) }}
{% endblock %}
{% block app_js %}
//Same as the other question
<script>
function FormCollection(div_id)
{
// keep reference to self in all child functions
var self=this;
self.construct = function () {
// set some shortcuts
self.div = $('#'+div_id);
self.div.data('index', self.div.find(':input').length);
// add delete link to existing children
self.div.children().each(function() {
self.addDeleteLink($(this));
});
// add click event to the Add new button
self.div.next().on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// add a new tag form (see next code block)
self.addNew();
});
};
/**
* onClick event handler -- adds a new input
*/
self.addNew = function () {
// Get the data-prototype explained earlier
var prototype = self.div.data('prototype');
// get the new index
var index = self.div.data('index');
// Replace '__name__' in the prototype's HTML to
// instead be a number based on how many items we have
var newForm = prototype.replace(/__name__/g, index);
// increase the index with one for the next item
self.div.data('index', index + 1);
// Display the form in the page in an li, before the "Add a tag" link li
self.div.append($(newForm));
// add a delete link to the new form
self.addDeleteLink( $(self.div.children(':last-child')[0]) );
// not a very nice intergration.. but when creating stuff that has help icons,
// the popovers will not automatically be instantiated
//initHelpPopovers();
return $(newForm);
};
/**
* add Delete icon after input
* #param Element row
*/
self.addDeleteLink = function (row) {
var $removeFormA = $('<i class="entypo-trash"></i>');
$(row).find('select').after($removeFormA);
row.append($removeFormA);
$removeFormA.on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// remove the li for the tag form
row.remove();
});
};
self.construct();
}
</script>
<script>
$(document).ready(function() {
new FormCollection('col-servliens');
new FormCollection('col-ports');
});
</script>
And I get
Variable "ports" does not exist.
I really need some help.. Collections are actually my nightmare...
Thanks !
Let me start with apologies, because my answer in the post you mention was clearly wrong :) At least the example code was incorrect. I wonder if the original poster ever found the right solution.
The error is easy enough to spot. In the Twig you use the variable ports, but how should Twig know where it comes from?
{% for servLiens in form.servLiens %}
<li>{{ form_row(servLiens) }} </li>
<ul id="col-ports" data-prototype="{{ form_widget(ports.vars.prototype)|e }}">
{%for ports in servLiens.ports %}
<li>{{ form_row(ports) }}</li>
{% endfor %}
{% endfor %}
Using {{ form_row(servLiens) }} will actually already create the collection classes, so the whole <ul> inside it is not needed. In fact Symfony is smart enough that you don't even need the first <ul>. It will find any child forms (ex. collections) and create all the HTML needed.
So the only thing you need to have in your code is:
{{ form_row(form.servLiens) }}
If you would look at the HTML generated from that, you will see the data-prototype and the children. It will automatically have an id attribute that you can use.
Problem is the javascript part. You are using a collection within a collection. The javascript FormCollection class (which I wrote) cannot handle this as simply as you've tried it. It will handle the top collection fine, but it will not automatically instantiate collections on load/create/delete. Remember that each servLiens collection has a collection of ports. Therefore already at loading you can't simply load the col-ports as you do now, but would need to load each port collection. Then you'd need to instantiate a new FormCollection on the port collection every time you add a servLiens row.
I don't have time to create your code for you, but I hope my explanation helps you to find a solution.
Looks like you use symfony 2.6 or older.
I would start by saying you should'nt do "'type' => new PortLienAppliServType()" but pass the form type name. Symfony will instantiate it and it could be the problem.
Everything is described in extend here: https://symfony.com/doc/2.6/reference/forms/types/collection.html#basic-usage
Honestly, Collections are not that difficult you just need to understand how it works then Symfony will manage everything for you.
I have the following setup:
Entity: Customer
Entity: Account
Entity: Message
Now imagine the following problem:
The account 'Mark' is in charge of two customers, 'Ben' and 'Lili'.
The account 'Tim' is in charge of two other customers, 'Tom' and 'Ronny'.
The account 'Ben' now wants to send a message to his customers. In a form he can choose the customers he would like to send the message to. Those will be saved as an ArrayCollection in the Message entity (in relation with entity Customer).
However, later on account 'Tim' can view this message and also send it to his customers the same way - by adding his customers to the list of recepients.
Problem is: When 'Tim' adds his recepients, he should not see the recepients of 'Ben' as this is none of his concern.
Visual explanation: http://jsfiddle.net/q0nn62o5/
My solution so far:
I created a custom FormType called 'AccountCustomerType'. This FormType is an entity which includes the customers of one particular account as choices:
$builder
->add('customer', 'entity', array(
'class' => 'AppBundle:Customer',
'choices' => $this->customers,
));
This FormType is used in the main form as a collection:
$form->add('recepients', 'collection', array(
'type' => new AccountCustomerType($customers),
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'by_reference' => false,
));
Printing form...:
<div class="recepients" data-prototype="{{ form_widget(form.recepients.vars.prototype)|e }}">
{% for customer in form.recepients %}
<div>
{{ form_widget(customer) }}
</div>
{% endfor %}
</div>
One problem left:
I can now choose from the customers that one account is in charge of. However, the recepients I am not in charge of are still shown as blank select fields. How can I hide these? I don't want to duplicate messages to seperate recepients as there are a couple more features connected to this.
You can filter a collection in a form by restricting the query results.
E.g. something like:
$accountData = $this->getEntityManager()
->createQueryBuilder()->select('a, c')
->from('YourAccountBundle:Account', 'a')
->join('a.customers', 'c') // assuming there is a relationship like this
->where('a = :yourAccountManager')
->setParameter('yourAccountManager', $accountEntity)
->getQuery()->getResult();
Then use $accountData in your parent form.
This will restrict the Customer entities shown in the form to only the ones linked to $accountEntity.
Note this needs to be the first fetch of this relation in your page load, if you lazy load it with doctrine then it'll return all Customer entities regardless of filtering.
For starters, I'm new to symfony2 -> coming from Laravel and ZF1 I have to admit it is not what I expected. I had a lot of struggle with simple tasks and by the moment of writing this twig editing is still a pain.
So scenario:
I have a invoice form with 2 datetime fields in it, I really hate that ugly 3 select field that is generated so I included my jQuery datepicker.
http://tinypic.com/r/eg5kyg/8
For my initial insertion of data because of the datetime I needed to create my form with a input type "text" :
$form=$this->createFormBuilder($invoice)
->add('date_ref','text', array(
'required'=> true))
->add('date_sent','text', array(
'required' => true ))
In this way that ugly 3 select datepicker is not shown and my users can select a date in an elegant way.
All good until the edit form:
I don't know why I didn't managed to get this done as i initially wished :
$invoice = $em->getRepository('AppBundle:Invoice')->find($id);
$form=$this->createFormBuilder($invoice)
->add('date_ref','text', array(
'required'=> true))
->add('date_sent','text', array(
'required' => true ))
->add('value','integer',array('required'=>true))
->add('nr_ref','text',array('required'=>true))
So i wanted to create a form from a entity with his properties but in the view
<div class="form-group">
{{ form_label(theForm.date_ref, 'Date Refferenced', { 'label_attr': {'class': 'control-label col-md-5'} }) }}
{{ form_errors(theForm.date_ref) }}
<div class="col-md-6">
{{ form_widget(theForm.date_ref,{'attr': {'class': 'form-control', 'data-provide':'datepicker'}}) }}
</div>
</div>
Was not working so I couldn't create my Edit Form as my first form with the date_ref input with a "text" value.
I searched and found out that creating a form from a entity data you need to create a "Type" so I created my "InvoiceType"
which has :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('is_paid')
/* not working
->add('date_ref', 'date', array('input' => 'datetime',
'format' => date('y-M-d'),
'widget' => 'single_text'))
*/
->add('date_ref') //this is a datetime obj which results in those 5 selects
->add('date_sent')
->add('value')
->add('nr_ref')
->add('name_provider')
;
}
$invoice = $em->getRepository('AppBundle:Invoice')->find($id);
$form = $this->createForm(new InvoiceType(), $invoice);
But because this method creates a form from the entity, if I change the type from the buildForm() function .. I get an error that he expects a Object of Datetime instead of a string ("text").
So now I am stuck at making the Edit Form .. that would be a type, 1 input Text and pre-populated with the date from the Database(something like 2014/02/02) using datepicker and when you click on it, it should appear as in the create form.
Like I said I am new, I learned a lot of things that I normally done other ways in other MVC and I struggle a lot with this datetime Object and symfonys 3 select datetime.
http://tinypic.com/r/28bz5h4/8
<- This looks awful
Thanks a lot Khalid Junaid, after I looked up the post you have mentioned I finally got it working as i wanted:
Solution:
->add('date_ref', 'date' ,array(
'widget'=> 'single_text',
'format'=>'d/M/y'))
->add('date_sent','date' ,array(
'widget'=> 'single_text',
'format'=>'d/M/y'))
I know that you can split a form out in twig and choose to not render the label for a particular field, but I can't help but think that you must be able to do this from the form class. The 'label' key in the options array lets you change this value to whatever you like, but passing either false or an empty string just returns the field name (see examples below where 'roles' is rendered as the label).
$builder
->add('roles', 'entity', array(
'class' => 'Acme\UserBundle\Entity\Role',
'label' => ''
));
$builder
->add('roles', 'entity', array(
'class' => 'Acme\UserBundle\Entity\Role',
'label' => false
));
Strangely, passing an empty space (which feels very dirty) seems to render a completely empty label, with no space even when viewing the source. Can anyone shed any light on the best approach, or even why the empty space seems to work?
Since Symfony 2.2 you can avoid the <label> rendering using the false value for the label attribute:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Name', null, array('label' => false))
;
}
Source
Keep your 'View' specifications separate from your 'Model'
If you follow the accepted answer which says:
$builder
->add('Name', null, array('label' => false))
;
your form is not as re-usable. Especially if your form appears in more than one location (or might in the future).
If you do not want to render the form label it is best to do so in Twig (assuming your using Twig).
instead of rendering {{ form_row(form.name) }}, render each element separetly and exclude the form_label
ex.
{{ form_errors(form.name) }}
{# {{ form_label(form.name) }} <-- just dont include this #}
{{ form_widget(form.name) }}
If down the road you wanted the label in one instance of the form but the not the other, simply adding {{ form_label(form.name) }} would suffice; Where as changing array('label' => true) would turn the label on everywhere
If you are rendering your form with the one liner {{ form(form) }} then you should have a look at the symfony docs
Just add {'label':false} to your form_row()
{{ form_row(form.name, {'label':false}) }}
I don't understand very well your question but in form to show the name of label,personnaly I do like that :
$builder
->add('role', 'text')
in my twig :
<tr>
<td>{{ form_widget(form.role) }} </td>
<td>{{ form_label(form.role, "Name of Label") }}</td>
</tr>
<tr>
<td>{{ form_errors(form.role) }}</td>
</tr>
To hide my label, I had to render just the widget for the field, and not the label, e.g.
{{ form_widget(edit_form.event) }}
{{ form_rest(edit_form) }}
The problem with the ' ' label with a space in, is that it still renders the html input which is there and affects the page.
this should work (although its not a very clean solution)
$builder
->add('roles', 'entity', array(
'class' => 'Acme\UserBundle\Entity\Role',
'label' => ' '
));
(note the space between the ticks)