Another Twig Ternary Example

Introduction

Previously in my Simplified Web Development with JSON and the Twig Ternary Operator, I had used JSON that had boolean values. Recently I’ve created another form that has a survey with checkboxes and I stored the JSON with string values instead. This article gives an alternate example of using the Twig ternary operator to handle strings.

The JSON Code

My survey JSON sample looks like the following example:

JSOM_Sample

In the above JSON, the keys represent the survey questions, and the string values represent the checkbox that was chosen. In the above case “strong_A” means “Strong Agree”, and “some_D” means “Somewhat Disagree”. It’s just a simpler way to write/code the JSON and still keep it understandable.

I struggled for a long time on how to handle processing of the JSON data in my Twig template when presenting a view of the form that was submitted. I show the results of the submitted for in a html table with a table row for each question and the selected checkboxes. So one row consists of the question + checkbox1 + checkbox2 + checkbox3 + checkbox4, and a total of 4 questions. So to do this in Twig, I struggle with how to create the for loop.

I thought maybe I could do a Twig for loop using {% for i in 0..3 %} to process each of the 4 checkboxes, and thus I was thinking of using the following JSON code:

JSOM_Sample_Alt

So in the above I would store each checkbox as a boolean in the JSON array, however, this would actually require a lot more Twig code, and a lot of if/else statements. I wanted to simplify my code and reduce the number of lines required.

Problems with Twig If Statements

One way of processing whether any of the checkboxes is selected is by using if/else statements, however, the code gets messy and looks something like the following code sample:

Twig_If_Probs

Notice each html td element has an if/else statement, and a total of 32 lines are used for one table row. This is a lot of code.

Simplifying the Code with the Twig Ternary Operator

I simplified the code by using the Twig ternary operator (as described in the documentation) to replace all the if/else statements. For example, to check my JSON in Twig to see if the “Strongly Agree” checkbox is selected, I use the following code in my html td element:

code

Where in the above “(survey[‘Recommend’] == ‘strong_A’)” does a comparison to see if it matches the string, and if so then show a checked checkbox. The code CheckCode is an html escape code to render a checked checkbox. The code UncheckCode is an empty checkbox.

Example in Twigfiddle

I created a Twigfiddle here to show you the twig working in case you need to see it in action. Also, you will need to see the result in html, so there’s a twig fiddle showing the resultant rendered html in this SyncFiddle.

Enjoy!

Survey Form in Symfony

Introduction

I’ve recently had to develop a form in a Symfony application that has a section of it with survey type questions. In Symfony it’s not so obvious that the ChoiceType field type is the most suitable type, but it definitely is the one to use. This article describes how to create the survey form.

Original Requirements

I didn’t have any written down requirements other than the existing form, and below is a screenshot of what that looks like:

original_survey

As you can see, it uses checkboxes; which is not appropriate. The reason being is that normally in a group of checkboxes, you can check any or all of them. In a survey, you only want to check one of them. So a radio button is the best choice.

Also look closely at the headings. The problem is the order. The order should be from one extreme to another. So instead this order:

Strongly Agree — Somewhat Agree — Somewhat Disagree — Strongly Disagree.

So my form would need to incorporate those need requirement changes.

ChoiceType in Form Class

In my form class, I initially used the following sample code for the “recommend” set of radio button choices:

->add('sur_recommend', ChoiceType::class, array(
    'mapped' => false,
    'label' => 'I would recommend this program to my friends and '.
               'family as a great opportunity',
    'choices' => array(
            'strong_A' => 'strong_A',
            'some_A' => 'some_A',
            'some_D' => 'some_D',
            'strong_D' => 'strong_D',
    ),
    'expanded' => true,
    'multiple' => false,
))

With the two settings “expanded” set to true and “multiple” set to false, this sets the choice group to radio buttons. The “mapped” set to false just means that “sur_recommended” is not a Doctrine database column, so the data is not mapped. I keep the values and the keys the same, since I don’t want to show any labels in my case.

Unfortunately with the above code, it doesn’t quite work for my case. If I render in Twig using just this code:

{{ form_widget(form.sur_recommend) }}

Then the resultant output will render like the following screenshot:

buttons_w_labels

I don’t want those labels appearing at all, but instead in a column header on the top.

Fixing the Code

In order to prevent the labels from showing, you should set “choice_label” to false, like the following code:

->add('sur_recommend', ChoiceType::class, array(
    'mapped' => false,
    'label' => 'I would recommend this program to my friends and '.
               'family as a great opportunity',
    'choice_label' => false,
    'choices' => array(
            'strong_A' => 'strong_A',
            'some_A' => 'some_A',
            'some_D' => 'some_D',
            'strong_D' => 'strong_D',
    ),
    'expanded' => true,
    'multiple' => false,
))

Then when rendered the labels no longer appear. The other problem is, we have a label for the choice group, but wee just want it to appear on the left side, and the buttons on the right. We also need the buttons to each appear under the names of each of the column headings. This needs to be done in Twig, which is described next.

Using Twig to render Properly

Twig is a powerful rendering language, and it can greatly simply any of your programming efforts. So I highly recommend learning how to use it properly. You can always use Twigfiddle to experiment first with code that you need to write and also to help you learn.

Since I don’t render each of the choice values (which would be labels), I need to put a header column on the top, and put each of the choice group labels on the left. Then each of the radio buttons need to line up under the header columns. To do this, a html table is a good idea.

Here is the Twig code that I came up with:

<fieldset>
<legend>Survey:</legend>
   <table>
      <tr><td></td><td>Strongly Agree</td><td>Somewhat Agree</td>
          <td>Somewhat Disgree</td><td>Strongly Disgree</td></tr>
      <tr style="text-align: center;"><td>{{ form_label(form.sur_recommend) }}</td>
         {% for i in 0..3 %}
            <td>{{ form_widget(form.sur_recommend[i]) }}</td>
         {% endfor %}
      </tr>
      ...

   </table>
</fieldset>

Notice the heading column is in one row, and I have a td element for each heading. I use the form_label to show the choice group label on the left. Notice I have a for loop, and a td element for each radio button as part of the choice group.

The resultant code renders on a page as follows:

final_survey

This is quite functional and looks good and similar to the original.

Using the null-coalescing Operator in Twig

Introduction

Recently, I wanted to simplify my Twig code by only showing a filled in “Other” value when an “Other” checkbox had been selected and the Other value filled in within a Symfony form that I developed. You can read up on how I developed that form in my Simplified Web Development with JSON and the Twig Ternary Operator.

My JSON code looks like the following:

{
   "License": true,
   "Transit": true,
   "No_Issues": false,
   "Friend": true,
   "Car": false,
   "Other": true,
   "Other_Value": "skateboard"
}

Where in this case “Other” is set to “true”, so Other was selected and the Other Value was filled in and “skateboard” was entered in the form.

Rending in Twig

So now in my Twig template, I can use the null-coalescing operator which is in the form:

{{ foo ?? 'no' }}

The above code means, if foo is NOT defined, then show ‘no’. So in my case for the JSON I was using, I used the null-coalescing operator in my Twig template like so:

Other: {{ transport['Other_Value'] ?? '' }}

Which will simply display “skateboard” in this case. The code is very simple and is easy to re-use and support.

Hope this helps someone!

 

Simplified Web Development with JSON and the Twig Ternary Operator

Introduction

I’ve been looking at ways to simplify my code, make it easier to support, make it easier to use, and easier to maintain. I have found that JSON is an ideal way to store a large amount of data in databases, and Twig is an ideal templating engine to make use of JSON data.

Example JSON Representation

Here is a real example where we have a form that has a number of checkboxes that can be checked. In this case, you can check all of the checkboxes (except the no issues one).

Transport_Section

You could design your database to have a column for each checkbox, but that would be a unwise choice; since for example just this section has 6 checkboxes, and then you will need to process all 6 of those checkboxes and whether they are checked of not.

The better way to go is to store this in JSON format in the database. For the checkbox “I don’t have a driver’s license”, we could call that “License” and it can be either true or false (whether it is checked or not). For the checkbox “I will be riding with a friend to college”, we could call that “Friend”.

So I came up with the resultant format for the JSON to store in my database like so:

{
   "License": false,
   "Transit": true,
   "No_Issues": false,
   "Friend": false,
   "Car": false,
   "Other": true,
   "Other_Value": "skateboard"
}

Notice also for the “Other:” checkbox, I made it a boolean; and if it is true, there is a “Other_Value” object that stores the string value that gets entered. Essentially I use Javascript to check when the “Other:” checkbox is selected and then I will show a input field to enter the Other value, and I store that string.

Handling the JSON in the Controller

In my case, I use Symfony as my MVC framework to develop my forms and I have to first store the form booleans as a PHP array:

// Create transportation JSON.
$transport_json = array(
   "License" => $license_bool,
   "Transit" => $transit_bool,
   "No_Issues" => $trans_no_issues_bool,
   "Friend" => $friend_bool,
   "Car" => $car_bool,
   "Other" => $trans_other_bool,
   "Other_Value" => $trans_other_value
);

Then I use the standard PHP function json_encode to store the Transportation JSON data in my assessment object:

// Set the tranport JSON in the assessment.
$assess->setTransport( json_encode($transport_json) );

Also, you need to persist (uses Doctrine) the assessment object to the database:

// Save the assessment to db.
$em->persist( $assess );
$em->flush();

Then to pass the saved JSON back from the database and view in a Twig template, we use the standard PHP json_decode function to pass the JSON data as a parameter to the Twig assessment template:

return $this->render('default/viewIndNeedsAssess.html.twig', array(
        'stu' => $student,
        'assess' => $student->getIndneedForm(),
        'transport' => json_decode($student->getIndneedForm()->getTransport(), true),
        ...
));

Handling the JSON in Twig with Ternary Operator

Now finally since we can reference the “transport” JSON array in the Twig template, we can easily use the ternary operator to display a view of the completed form. In my case I wanted to show the form results in a html table within “td” elements, I wanted to show checkbox icons that show whether the form is checked or not.

Here is the Twig code I used to achieve the best results:

<td>
   <b>Transportation:</b>

    {{ transport['License'] ? '☑' : '☐' }}
      I don't have a driver's license

    {{ transport['Transit'] ? '☑' : '☐' }}
      I will be using the Kern Regional Transit / Taft Area Transit

    {{ transport['No_Issues'] ? '☑' : '☐' }}
      I have no transportation issues

    {{ transport['Friend'] ? '☑' : '☐' }}
      I will be riding with a friend to college

    {{ transport['Car'] ? '☑' : '☐' }}
      I have my own car

    {{ transport['Other'] ? '☑' : '☐' }}
      Other: {{ transport['Other_Value'] ?? '' }}</td>

Notice in the above ternary, I check if “transport[‘License’]” is true, if it’s true, then I show the code “☑” which is a checked checkbox.

And here is a screenshot with the final way the form looks when viewing a completed (submitted) form:

Transport_Final

Display Checkbox Group Properly in Twig

Introduction

In a form I’ve recently worked on using Symfony, I had to show a group of selectable checkboxes. In this case, the user can select any number of checkboxes an also in one group, two of the checkboxes I needed to use Javascript to detect when the box was selected. I wrote about that in my Javascript Code Refactoring article.

There are two types of problems when using a ChoiceType built-in Field Type set to element type of checkboxes in Symfony:

  1. Rending in Twig is not easy.
  2. Setting attributes is not easy, and may not work as you expect.

The above two reasons are why I decided to write this article.

Example Code

Below is an example checkbox group that I created in a form class for some employment questions that are asked in a form:

public function buildForm(FormBuilderInterface $builder, array $options)
{
   $builder
      ...
      // Employment
     ->add('employ', ChoiceType::class, array(
           'mapped' => false,
           'required' => false,
           'expanded' => true,
           'multiple' => true,
           'label' => 'Employment',
           'choices' => array(
                   'I have a job. # of hours/week:' => 'have_job',
                   'I am work study eligible' => 'work_study',
                   'I need assistance in finding a job' => 'find_work',
                   'I need to learn interviewing skills' => 'interview',
                   'I have no employment needs at this time' => 'no_needs',
                   'I volunteer for a non-profit organization' => 'non_profit',
                   'I need assistance with my resume' => 'resume',
                   'I need assistance finding an internship' => 'intern',
                   'I am undecided about my career or major' => 'major',
                   'Other:' => 'other',
            ),
      ))
      ...
}

Then when I tried to render in a single html table row in Twig, using code like this:

<tr><td>{{ form_widget(form.employ) }}</td></tr>

Then this appears like this rendering in a web page:

TwigRender

Notice the problem in that each checkbox appears inline directly after each other. I tried to use the Twig nl2br filter together with “\n” in my choice values, but that didn’t work since I really need another table row to show each of the checkboxes and labels.

Adding Checkboxes to TR

Since a ChoiceType in a Symfony form is an array of checkboxes, then we can use a Twig for loop to properly show the widget and label in a HTML table row. The Twig code needed is as follows:

{% for i in 1..8 %}
   <tr><td>&emsp;{{ form_widget(form.employ[i]) }}{{ form_label(form.employ[i]) }}</td></tr>
{% endfor %}

The unfortunate part of the above code is, in order to add an attribute like a Javascript onchange, this has do be done in Twig on the particular element like so:

<tr><td>&emsp;{{ form_widget(form.employ[0],{'attr':{'onchange':'changeJobHours()'}}) }}
   {{ form_label(form.employ[0]) }}&emsp;
   {{ form_label(form.job_hours) }}{{ form_widget(form.job_hours) }}</td></tr>

The above adds an onchange attribute for the first checkbox. I also have a job hours and label, but those are hidden and shown when a onchange event is triggered and box is selected.

Resultant Code

The overall resultant code looks like the following:

<table id='6th' style='border: 1px solid; margin-left: auto; margin-right: auto; width: 50%; display: none;'
   background="{{ asset('images/indneeds_assess/employment2.png') }}">
   <tr><td colspan="2"><b>{{ form_label(form.employ) }}</b></td></tr>
   <tr><td>{{ form_widget(form.employ) }}</td></tr>
   <tr><td>&emsp;{{ form_widget(form.employ[0],{'attr':{'onchange':'changeJobHours()'}}) }}
      {{ form_label(form.employ[0]) }}&emsp;
      {{ form_label(form.job_hours) }}{{ form_widget(form.job_hours) }}</td></tr>
   {% for i in 1..8 %}
      <tr><td>&emsp;{{ form_widget(form.employ[i]) }}{{ form_label(form.employ[i]) }}</td></tr>
   {% endfor %}
   <tr><td>&emsp;{{ form_widget(form.employ[9],{'attr':{'onchange':'changeEmploy()'}}) }}
      {{ form_label(form.employ[9]) }}&emsp;
      {{ form_label(form.employ_other) }}{{ form_widget(form.employ_other) }}</td></tr>
</table>

The above code puts each checkbox in on HTML tr elements. The resultant web page looks like the following and appears much better than the original.

Employment

Rendering Semester & Year in Symfony Form

Introduction

In a form I’m working on recently, I needed to show the following info (see screenshot below) in a Symfony form:

render_semester_year

It’s from a paper form, and I’m converting to electronic. Since the semester is supposed to be circled, this means using a drop-down list allowing selecting only one item; and instead of filling in the year, have a drop-down list with a reasonable range of years.

Form Type Code

In Symfony, you normally create Form classes, and because the above dates don’t map back to an Entity attribute, I set ‘mapped’ to false in the FieldTypes that I use. So for the above, I used a ChoiceType for the semester, and a DateType for the year. I happen to show the form elements in a table, and because the elements need to appear on the same row, I need to use a CSS style of “float:left”. The resultant code looks something like this:

class ApplicationType extends AbstractType
{
   public function buildForm(FormBuilderInterface $builder, array $options)
   {
      $builder
         ...
         ->add('target', ChoiceType::class, array(
            'mapped' => false,
            'expanded' => false,
            'multiple' => false,
            'label' => 'Target final semester at Taft College: (If unsure, approximate.)',
            'choices' => array(
               'Fall' => 'fall',
               'Spring' => 'spring',
               'Summer' => 'summer',
            ),
            'attr' => array(
               'style' => 'float:left',
            ),
         ))
         ->add('target_date', DateType::class, array(
            'mapped' => false,
            'format' => 'dd MMM y',
            'label' => 'year:',
            'label_attr' => array(
               'id' => 'target_date',
               'style' => 'float:left;margin-left:8px',
            ),
            'years' => range(2025,2017,1),
            'attr' => array(
               'style' => 'float:left',
            ),
         ))
      ...
      ;
   }
}

You’ll notice that for the format option I had to use “dd MMM y”. If you use something like “yyyy” instead, you get this kind of Symfony exception error message:

The “format” option should contain the letters “y”, “M” and “d”. Its current value is “yyyy”.

So In other words, so you have to use each of year, month, and date placeholders for the format specification.

Presenting Properly in Twig

Unfortunately, the above code will also show the date and the month of the “target_date” form element. The best thing to do is hide both of them. This can be done by using CSS “display:none” again.

Here is the Twig code I used to render correctly:

<tr><td>{{ form_label(form.target) }}</td></tr>
   {{ form_widget(form.target_date['day'],{'attr':{'style':'display:none'}}) }}
   {{ form_widget(form.target_date['month'],{'attr':{'style':'display:none'}}) }}
<tr><td>{{ form_widget(form.target) }}
   {{ form_label(form.target_date) }}{{ form_widget(form.target_date['year']) }}</td></tr>

You’ll notice in the above code, I rended the “target” label in one row, then I hide the day and month “target_date” widgets by setting each of their attributes, and then on another row I show the “target” widget and then the “target_date” label followed by only the “target_date” year. This effectively shows the semester and year side by side.

The result is like in the following screenshot:

result

 

Symfony Form Rendering Woes

Introduction

Recently for Taft College, I’ve been working on a TRIO program application which is built with Symfony PHP framework. I have various forms that needs to be filled out and submitted by students, and this post is about how I had rending issues of one of the forms and how I solve the problem.

The Form

In one of the forms I created, I used a Form class to create the form. On the form, there is a checkbox that when selected needs to show a date selection. There are two checkboxes by the High School area, namely “Diploma” and “G.E.D.” test. Below is a sample of the code I started with:

->add('hs_diploma', ChoiceType::class, array(
   'mapped' => false,
   'expanded' => true,
   'multiple' => true,
   'choices' => array(
      'Diploma' => 'diploma',
   ),
))
->add('hs_dip_date', DateType::class, array(
   'mapped' => false,
   'format' => 'dd MMM y',
   'label' => 'date:',
   'years' => range(2017,1960,1),
))
->add('ged', ChoiceType::class, array(
   'mapped' => false,
   'expanded' => true,
   'multiple' => true,
   'choices' => array(
      'G.E.D.' => 'ged_checked',
   ),
))
->add('ged_date', DateType::class, array(
   'mapped' => false,
   'format' => 'dd MMM y',
   'label' => 'date:',
   'years' => range(2017,1960,1),
))

And here is what the rendering looks like:

1st_render

The elements are in a html table and notice the way that the date label appears on one row and on the next row is the date picker, followed by the G.E.D. checkbox (and label and date picker). So the problem is that I need both of the checkboxes, labels and date pickers to all appear in one row.

Fixing in Twig?

Then my next thought would be could this be fixed in the Twig template? Twig is a great templating engine which allows you to customize the form rendering using certain markup. I figured I could customize it by specifying in this format:

Diploma checkbox widget:Diploma date label:Diploma date picker widget:GED checkbox widget:GED date label:GED date picker widget

All the above in one html table data () element, within a table row (

). This would look like the following code in Twig:

<tr><td>{{ form_widget(form.hs_diploma) }}{{ form_label(form.hs_dip_date) }}
 {{ form_widget(form.hs_dip_date) }}{{ form_widget(form.ged) }}{{ form_label(form.ged_date) }}
 {{ form_widget(form.ged_date) }}</td></tr>

However this doesn’t even change the way it renders as in the image above. I used FireFox browser tools and right-clicked on the table element and selected Inspect Element, which shows that within the element, each checkbox and date picker is with a

tag and the labels are outside of the div tags. This is why they don’t render properly.

So the end answer to my question is: It would be possible to fix this in Twig, but it would be a lot of code, and I was looking for an easier way.

Setting Attributes of Symfony Field Types

Symfony has a number of different Field Types, and in my form I use ChoiceType and DateType (as per the above code). I also use other types (of course), but in this blog I only will reference changes to these types.

In FireFox, I experimented with the browser tools by using the Inspector tab and adjusting the style attribute of the

tags, and then I found that if I used style=”float:left”, this fixed the problem of rendering on different rows in the table.

This is the code I used properly render the checkboxes:

->add('hs_diploma', ChoiceType::class, array(
   'mapped' => false,
   'expanded' => true,
   'multiple' => true,
   'choices' => array(
      'Diploma' => 'diploma',
   ),
   'attr' => array(
      'style' => 'float:left',
   ),
))

Notice that I add an attribute of style and set to “float:left”. Then for a date picker I use the following code:

->add('hs_dip_date', DateType::class, array(
   'mapped' => false,
   'format' => 'dd MMM y',
   'label' => 'date:',
   'label_attr' => array(
      'style' => 'float:left;margin-left:8px',
   ),
   'years' => range(2017,1960,1),
   'attr' => array(
      'style' => 'float:left',
   ),
))

Notice there are attributes for both the label and the date picker. Both attributes are needed. Notice also for the label_attr, I added a “margin-left:8px”; this was to add a space between (for example) Diploma and the label date.

Below is a render of what the changes look like after adding the styling changes:

2nd_render

Notice everything all appears on one row exactly as I need it.

Since I set the attributes in the form class, it’s very easy to support, since I merely only need to change the class file should I need to have to make any changes. I should mention that this would really be a lot of work if I had to make the changes in Twig; so I highly recommend you use the field type attributes when it is possible.