15 min read

(For more resources on ChronoForms, see here.)

Using PHP to create “select” dropdowns

One frequent request is to create a select drop-down from a set of records in a database table.

Getting ready

You’ll need to know the MySQL query to extract the records you need from the database table. This requires some Joomla! knowledge and some MySQL know-how.

Joomla! keeps its articles in the jos_content database table and the two-table columns that we want are the article title and the article id. A quick check in the database tells us that the columns are appropriately called title and id. We can also see that the section id column is called sectionid (with no spaces); the column that tells us if the article is published or not is called state and takes the values 1 for published and 0 for not published.

How to do it . . .

We are going to use some PHP to look up the information about the articles in the database table and then output the results it finds as a series of options for our drop-down box.

You’ll recall that normal HTML code for a drop-down box looks something like this:

<select . . .>
<option value='option_value_1'>Option 1 text</option>
<option value='option_value_2'>Option 2 text</option>
. . .
</select>

This is simplified a little so that we can see the main parts that concern us here—the options. Each option uses the same code with two variables—a value attribute and a text description. The value will be returned when the form is submitted; the text description is shown when the form is displayed on your site.

In simple forms, the value and the description text are often the same. This can be useful if all you are doing with the results is to display them on a web page or in an e-mail. If you are going to use them for anything more complicated than that, it can be much more useful to use a simplified, coded form in the value.

For our list of articles, it will be helpful if our form returns the ID of the article rather than its title. Hence, we need to set the options to be something like this:

<option value='99'>Article title</option>

Having the article ID will let us look up the article in the database and extract any other information that we might need, or to update the record to change the corresponding record.

Here’s the code that we’ll use. The beginning and ending HTML lines are exactly the same as the standard drop-down code that ChronoForms generates but the “option” lines are replaced by the section inside the <?php . . . ?> tags.

The PHP snippet looks up the article IDs and titles from the jos_content table, then loops through the results writing an <option> tag for each one:

<div class="form_item">
<div class="form_element cf_dropdown">
<label class="cf_label" style="width: 150px;">
Articles</label>
<select class="cf_inputbox validate-selection"
id="articles" size="1" name="articles">
<option value=''>--?--</option>
<?php
if (!$mainframe->isSite() ) {return;}
$db =& JFactory::getDBO();
$query = "
SELECT `id`, `title`
FROM `#__content`
WHERE `sectionid` = 1
AND `state` = 1 ;
";
$db->setQuery($query);
$options = $db->loadAssocList();
foreach ( $options as $o ) {
echo "<option value='".$o[id]."'>".$o[title]."</option>";
}
?>
</select>
</div>
<div class="cfclear">&nbsp;</div>
</div>

The resulting HTML will be a standard select drop-down that displays the list of titles and returns the article ID when the form is submitted. Here’s what the form input looks like:

A few of the options from the page source are shown:

<option value='1'>Welcome to Joomla!</option>
<option value='2'>Newsflash 1</option>
<option value='3'>Newsflash 2</option>
. . .

How it works…

We are loading the values of the id and title columns from the database record and then using a PHP for each loop to go through the results and add each id and title pair into an <option> tag.

There’s more…

There are many occasions when we want to add select drop-downs into forms with long lists of options. Date and time selectors, country and language lists, and many others are frequently used.

We looked here to get the information from a database table which is simple and straightforward when the data is in a table or when the data can conveniently be stored in a table. It is the preferred solution for data such as article titles that can change from day to day.

There are a couple of other solutions that can also be useful:

  • Creating numeric options lists directly from PHP
  • Using arrays to manage option lists that change infrequently

Creating numeric options lists

Let’s imagine that we need to create a set of six numeric drop-downs to select: day, month, year, hour, minute, and second. We could clearly do these with manually-created option lists but it soon gets boring creating sixty similar options.

There is a PHP method range() that lets us use a similar approach to the one in the recipe. For a range of zero to 60, we can use range(0, 60). Now, the PHP part of our code becomes:

<div class="form_item">
<div class="form_element cf_dropdown">
<label class="cf_label" style="width: 150px;">
Minutes</label>
<select class="cf_inputbox validate-selection"
id="minutes" size="1" name="minutes">
<option value=''>--?--</option>
<?php
if (!$mainframe->isSite() ) {return;}
foreach ( range(0, 60) as $v ) {
echo "<option value='$v'>$v</option>";
}
?>
</select>
</div>
<div class="cfclear">&nbsp;</div>
</div>

This is slightly simpler than the database foreach code, as we don’t need the quotes round the array values.

This will work very nicely and we could repeat something very similar for each of the other five drop-downs. However, when we think about it, they will all be very similar and that’s usually a sign that we can use more PHP to do some of the work for us.

Indeed we can create our own little PHP function to output blocks of HTML for us.

Looking at this example, there are four things that will change between the blocks—the label text, the name and id, and the range start and the range end. We can set these as variables in a PHP function:

<?php
if ( !$mainframe->isSite() ) {return;}
function createRangeSelect($label, $name, $start, $end) {
?>
<div class="form_item">
<div class="form_element cf_dropdown">
<label class="cf_label" style="width: 150px;">
<?php echo $label; ?></label>
<select class="cf_inputbox validate-selection"
id="<?php echo $name; ?>" size="1"
name="<?php echo $name; ?>">
<option value=''>--?--</option>
<?php
foreach ( range($start, $end) as $v ) {
echo "<option value='$v'>$v</option>";
}
?>
</select>
</div>
<div class="cfclear">&nbsp;</div>
</div>
<?php
}
?>

Notice that this is very similar to the previous code example. We’ve added the function . . . line at the start, the } at the end, and replaced the values with variable names.

It’s important to get the placement of the <?php . . . ?> tags right. Code inside the tags will be treated as PHP, outside them as HTML.

All that remains now is to call the function to generate our drop-downs:

<?php
if (!$mainframe->isSite() ) {return;}
createRangeSelect('Day', 'day', 0, 31);
createRangeSelect('Month', 'month', 1, 12);
createRangeSelect('Year', 'year', 2000, 2020);
createRangeSelect('Hour', 'hour', 0, 24);
createRangeSelect('Minute', 'minute', 0, 60);
createRangeSelect('Second', 'second', 0, 60);
function createRangeSelect($label, $name, $start, $end) {
. . .

The result tells us that we have more work to do on the layout, but the form elements work perfectly well.

Creating a drop-down from an array

In the previous example, we used the PHP range() method to generate our options. This works well for numbers but not for text. Imagine that we have to manage a country list. These do change, but not frequently. So they are good candidates for keeping in an array in the Form HTML.

It’s not too difficult to find pre-created PHP arrays of countries with a little Google research and it’s probably easier to use one of these and correct it for your needs than to start from scratch.

As we mentioned with the Article list, it’s generally simpler and more flexible to use a list with standard IDs (we’ve used two-letter codes below). With countries, this can remove many problems with special characters and translations.

Here are the first few lines of a country list:

$countries = array(
'AF'=>'Afghanistan',
'AL'=>'Albania',
'DZ'=>'Algeria',
'AS'=>'American Samoa',
. . .
);

Once we have this, it’s easy to modify our foreach . . . loop to use it:

foreach ( $countries as $k => $v ) {
echo "<option value='$k'>$v</option>";
}

If you are going to use the country list in more than one form, then it may be worthwhile keeping it in a separate file that is included in the Form HTML. That way, any changes you make will be updated immediately in all of your forms.

Using Ajax to look up e-mail addresses

It’s not very difficult to add Ajax functionality to ChronoForms, but it’s not the easiest task in the world either.

We’ll walk through a fairly simple example here which will provide you with the basic experience to build more complex applications. You will need some knowledge of JavaScript to follow through this recipe.

Normally, the only communication between the ChronoForms client (the user in their browser) and the server (the website host) is when a page is loaded or a form is submitted. Form HTML is sent to the client and a $_POST array of results is returned. Ajax is a technique, or a group of techniques, that enables communication while the user is browsing the page without them having to submit the form.

As usual, at the browser end the Ajax communication is driven by JavaScript and at the server end we’ll be responding using PHP. Put simply, the browser asks a question, the server replies, then the browser shows the reply to the user.

For the browser JavaScript and the server PHP to communicate, there needs to be an agreed set of rules about how the information will be packaged. We’ll be using the JSON (www.json.org) format.

The task we will work on will use a newsletter form. We’ll check to see if the user’s e-mail is already listed in our user database. This is slightly artificial but the same code can easily be adapted to work with the other database tables and use more complex checks.

Getting ready

We’ll need a form with an e-mail text input. The input id needs to be email for the following code to work:

<div class="form_item">
<div class="form_element cf_textbox">
<label class="cf_label"
style="width: 150px;">Email</label>
<input class="cf_inputbox" maxlength="150" size="30"
title="" id="email" name="email" type="text" />
</div>
<div class="cfclear">&nbsp;</div>
</div>

The form we use will also have a name text input and a submit button, but they are to make it look like a real form and aren’t used in the Ajax coding.

How to do it . . .

We’ll follow the action and start with the user action in the browser. We need to start our check when the user makes an entry in the e-mail input. So, we’ll link our JavaScript to the blur event in that input. Here’s the core of the code that goes in the Form JavaScript box:

/ set the url to send the request to
var url = 'index.php?option=com_chronocontact
&chronoformname=form_name&task=extra&format=raw';
// define 'email'
var email = $('email');
// set the Ajax call to run
// when the email field loses focus
email.addEvent('blur', function() {
// Send the JSON request
var jSonRequest = new Json.Remote(url, {
. . .
}).send({'email': email.value});
});

Note that the long line starting with var url = . . . &format=raw’; is all one line and should not have any breaks in it. You also need to replace ‘form_name’ with the name of your form in this URL.

There really isn’t too much to this. We are using the MooTools JSON functions and they make sending the code very simple.

The next step is to look at what happens back on the server.

The URL we used in the JavaScript includes the task=extra parameter. When ChronoForms sees this, it will ignore the normal Form Code and instead run the code from the Extra Code boxes at the bottom of the Form Code tab.

By default, ChronoForms will execute the code from Extra code 1. If you need to access one of the other boxes, then use for example, task=extra&extraid=3 to run the code from Extra Code box 3.

Now, we are working back on the server. So, we need to use PHP to unpack the Ajax message, check the database, and send a message back:

<?php
// clean up the JSON message
$json = stripslashes($_POST['json']);
$json = json_decode($json);
$email = strtolower(trim($json->email));
// check that the email field isn't empty
$response = false;
if ( $email ) {
// Check the database
$db =& JFactory::getDBO();
$query = "
SELECT COUNT(*)
FROM `#__users`
WHERE LOWER(`email`) = ".$db->quote($email).";
";
$db->setQuery($query);
$response = (bool) !$db->loadResult();
}
$response = array('email_ok' => $response );
//send the reply
echo json_encode($response);
// stop the from running
$MyForm->stopRunning = true;
die;
?>

This code has three main parts:

  1. To start with, we “unwrap” the JSON message.
  2. Then, we check if it isn’t empty and run the database query.
  3. Lastly, we package up the reply and tidy up at the end to stop any more form processing from this request.

The result we send will be array(’email_ok’ => $response ) where $response will be either true or false. This is probably the simplest JSON message possible, but is enough for our purpose.

Note that here, true means that this e-mail is not listed and is OK to use.

The third step is to go back to the form JavaScript and decide how we are going to respond to the JSON reply. Again, we’ll keep it simple and just change the background color of the box—red if the e-mail is already in use (or isn’t a valid e-mail) or green if the entry isn’t in use and is OK to submit.

Here’s the code snippet to do this using the onComplete parameter of the MooTools JSON function:

onComplete: function(r) {
// check the result and set the background color
if ( r.email_ok ) {
email.setStyle('background-color', 'green');
} else {
email.setStyle('background-color', 'red');
}
}

Instead of (or as well as) changing the background color, we could make other CSS changes, display a message, show a pop-up alert, or almost anything else.

Lastly let’s put the two parts of the client-side JavaScript together with a little more code to make it run smoothly and to check that there is a valid e-mail before sending the JSON request.

window.addEvent('domready', function() {
// set the url to send the request to
var url = 'index.php?option=com_chronocontact
&chronoformname=form_name&task=extra&format=raw';
var email = $('email');
email.addEvent('blur', function() {
// clear any background color from the input
email.setStyle('background-color', 'white');
// check that the email address is valid
regex = /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i;
var value = email.value.trim();
if ( value.length > 6 && regex.test(value) ) {
// if all is well send the JSON request
var jSonRequest = new Json.Remote(url, {
onComplete: function(r) {
// check the result and set the background color
if ( r.email_ok ) {
email.setStyle('background-color', 'green');
} else {
email.setStyle('background-color', 'red');
}
}
}).send({'email': email.value});
} else {
// if this isn't a valid email set background color red
email.setStyle('background-color', 'red');
}
});
});

Note that the long line starting with var url = . . . &format=raw’; is all one line and should not have any breaks in it. You also need to replace form_name with the name of your form in this URL.

Make sure both the code blocks are in place in the Form JavaScript box and in the Extra Code 1 box, save, and publish your form. Then, test it to make sure that the code is working OK. The Ajax may take a second or two to respond but once you move out of the e-mail, input by tabbing on to another input or clicking somewhere else; the background colour should go red or green.

How it works…

As far as the Ajax and JSON parts of this are concerned, all we can say here is that it works. You’ll need to dig into the MooTools, Ajax, or JSON documents to find out more.

From the point of view of ChronoForms, the “clever” bit is the ability to interpret the URL that the Ajax message uses. We ignored most of it at the time but the JavaScript included this long URL (with the query string broken up into separate parameters):

index.php
?option=com_chronocontact
&chronoformname=form_name
&task=extra
&format=raw

The option parameter is the standard Joomla! way to identify which extension to pass the URL to.

The chronoformname parameter tells ChronoForms which form to pass the URL to.

The task=extra parameter tells ChronoForms that this URL is a little out of the ordinary (you may have noticed that forms usually have &task=send in the onSubmit URL). When ChronoForms sees this, it will pass the URL to the Extra Code box for processing and bypass the usual OnSubmit processing.

Lastly, the format=raw parameter tells Joomla! to show the resulting code without any extra formatting and without adding the template code. This means that all that is sent back is just the JSON message. Without it we’d have to dig the message out from loads of surrounding HTML we don’t need.

LEAVE A REPLY

Please enter your comment!
Please enter your name here