Chapter 5. Simplifying HTML Pages and Forms

This chapter covers yet another way in which CI helps save your time and make your coding more rigorous and logical.

Firstly, we'll cover various ways of building views the pages that control how you see the results prepared by your controllers and models.

Next, you'll see how to create HTML forms quickly, and with built-in safeguards; and you'll also see how to validate your forms.

I'm assuming that readers of this book are familiar with HTML and CSS. The following examples are very simplified, so we can focus on the CI code. And I have assumed that we have already written a CSS file and tucked it away somewhere on our site.

Writing a View

Views control how the user sees your website. They make it easy for you to present a consistent interface, and to change it if you need to. One of the advantages of MVC is that you separate presentation from logic, keeping everything much cleaner.

So far, all we've done is to look at the very simple 'Welcome' view that installs out of the box when you first load CI. (See Chapter 3.) Now let's look at how to make it more elaborate.

A view is just a set of HTML shelves to hold your content. The shelves may be one color or another; there may be lots of little ones, or just a few widely-spaced, elegant ones. But the view doesn't know or care what data is on those shelves. Like certain politicians, it is only interested in presentation.

To create a view, firstly you need to create a skeleton HTML web page as a PHP file. Let's call it basic_view.php. Save it in your application/views folder. (The reason for saving it in this folder is simply because the loader file looks for it there.)

<html>
<head>
</head>
<body>
<p>Hello world!</p>
</body>
</html>

Then you just load it from a controller when you want to use it, using $this->load ->view() inside an appropriate function:

function index()
{
 $this->load->view('basic_view'),
}

Note that if this were a model or a helper, you'd load it first, and then call it separately when you wanted to use it. With a view, calling it loads it as well, so you only need one line of code.

Of course, that's an empty view. To make it useful, we need content. Say we want to add a title and some text. First we define them in the controller:

function index()
{
$data['mytitle']  = "A website monitoring tool";
$data['mytext']  = "This website helps you to keep track of the   
other websites you control."; 
}

Notice how we have defined them not as separate scalar variables, but as elements of the array $data (or any other name we care to give it.). For the first array entry, the 'key' is'mytitle' and the 'value' is "A website monitoring tool".

Next, we call the view loading function:

function index()
{
$data['mytitle'] = "A website monitoring tool";
$data['mytext'] = "This website helps you to keep track of the other websites you control.";
}

We have made the $data array into a second parameter of the $this->load->view() function, after the name of the view itself. Once the $data array reaches the view, CI uses PHP's extract() function to turn each element of the $data array into a separate variable, with the 'key' as the variable name, and the 'value' as the variable value. These variables can then be referenced directly in our view:

<html>
<head>
</head>
<body>
<h1 class='test'><?php echo $mytitle; ?> </h1>
<p class='test'><?php echo $mytext; ?> </p>
</body>
</html>

You can only pass one variable of data to a view, but by building up an array of arrays, you can pack large amounts of information neatly into that one variable. It seems complicated, but is actually a tightly structured and elegant way of passing information.

Long and Short PHP Syntax

Before we go on, a note about different forms of PHP syntax.

The normal way to include a PHP 'code island' in the midst of HTML code is like this:

<?php echo $somevariable ?>

However, if you don't like this, CI also supports a shorter version:

<?=$somevariable?>

In this case, the external brackets delimiting the code island have lost the letters PHP (they are just<? ?>); and echo has been replaced by =. You can also use shorter syntax for if, for, foreach, and while loops. Complete instructions are in the online User Guide.

Personally, I prefer to stick to the standard format because I'm used to it. If you use the short format, note that some servers won't interpret the abbreviated format correctly. If you still wish to use the short tags, then go to your config file, and alter the line:

$config['rewrite_short_tags'] = FALSE;

to TRUE. CI will then rewrite short tags to the normal form before it sends them to the server. However, if there is a PHP error, using this re-writing function makes the error messages less meaningful, so debugging may be more difficult. As I say, I prefer to stick to the standard format.

For the record, CI also has a 'template parser' class, which allows you to put variables in your HTML code without any actual PHP coding at all. I haven't covered this. It is largely useful if you are working with HTML designers who may be confused by PHP code. Details of this class are available in the User Guide.

Nesting Views

So far, whether we use long or short PHP formats, this is pretty crude HTML. It would be nice, for instance, to put more information in the<head> section of the page. Better still if this could be a standard chunk of each page. Once again, something we only have to write (or alter) once, and can then re-use, nesting this view inside other views whenever we need the boring HTML header stuff.

Let's create a page header 'view' for our site, which displays a standard page header, as well as HTML declarations and meta information.

First, we type out the code for our 'nested' header view:

<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'><html xmlns='http://www.w3.org/1999/xhtml'>
<title><?php echo $mywebtitle ?></title>
<base href= <?php echo "$base"; ?> >
<?php echo $myrobots ?>
<link rel="stylesheet" type="text/css" href="<?php echo "$base/$css;?>">

Save this as views/header_view. It introduces new variables:

  • $mywebtitle, which is the page title (the meta tag; this won't show up on the screen, but search engines will read it. It may vary from page to page, so I've made it a variable.)

  • $myrobots, which I'm using for the standard instruction to robots that this site is not to be indexed.

  • $base and $css, which describe the base URL and the extra URL for our .css file, the stylesheet we're pretending we've already written, which lets us apply formatting consistently. These variables will be generated from data we have already stored in the CI.config file. (I could also have used the CI config variable site_url instead of base.)

What we need to know now is:

  • How do we call the second 'nested' view?

  • How do we assign values to its variables?

There are two options. Firstly, calls to views can be made from within other views. So our main view, basic_view, just needs a new line:

<html><head>
<?php $this->load->view('header_view'), ?>
</head><body>
<?php echo $mytitle; ?>
<?php echo $mytext; ?>
</body>
</html>

As for the variables, they can be assigned by two new lines in the original controller:

function index()
{
$data['mytitle'] = "A website monitoring tool";
$data['mytext'] = "This website helps you to keep track of the other websites you control.";
$this->load->view('basic_view', $data);
}

Here the new variables $myrobots, $css, $base, and $mywebtitle are created as new elements of the existing $data array, passed to basic_view, which unpacks them, and then made available to header_view when this is called by basic_view. (Just remember not to use the same variable name in two views that you are nesting, or one will overwrite the other.)

A second way is to add the view from inside the controller, by assigning it to a variable:

function index()
viewsnested views{
$data['mytitle'] = "A website monitoring tool";
$data['mytext'] = "This website helps you to keep track of the other websites you control.";
$data['myrobots'] = '<meta name="robots" content="noindex, nofollow">';
$data['mywebtitle'] = 'Web monitoring tool';
$data['base'] = $this->config->item('base_url'),
$data['css'] = $this->config->item('css'),
$data['header']    = $this->load->view('header_view', '', TRUE);
$this->load->view('basic_view', $data);
}

This is probably more correct from a strict MVC perspective.

There are actually three parameters you can pass with the load->view function.

  • The first, header_view, is the name of the view to be loaded. This is essential.

  • The second, which is optional, is the data to load into it.

  • The third is a Boolean value. If you don't specify it, this defaults to FALSE, and the view is sent to the browser. However, if you are nesting the view this way, you want it returned as a string to nest inside the variable you are passing to the host view. Setting the third parameter to TRUE achieves this.

Now we've got a reference to the stylesheet built in, we can update the view to use display classes that we might have defined there:

<html><head>
<?php $this->load->view('header_view'), ?>
</head><body>
</body>
</html>

Notice again how CI's MVC system allows you to separate display from content. The views only provide 'shelves' for the content, and even the styling of those shelves is derived from a .css stylesheet.

The view doesn't care what $mytext says, it just displays it on the right shelf with the right formatting. The controller that defines $mytext doesn't even know (or care) how the information it generates is displayed.

So, if we need to change the look of our pages, or display them on a different system (say WAP), then we only need to change one view and one CSS stylesheet. We don't need to mess around inside the code of several controllers.

And if we want to change the information displayed on the page, we don't need to touch the views, and remind ourselves to change some variable in each page we've written. We just change what the controller pushes out.

Remember the 'loose coupling' principle? Once again, this makes it easier to design, upgrade, and maintain your sites.

Practical Issues of Site Architecture

Wait a moment, you're saying in our header_view, we generated the CSS stylesheet address dynamically:

<link rel="stylesheet" type="text/css" href="<?php echo "$base/$css";?>">

This means that the controller had to produce this data, which is only relevant to how the information is displayed, and we've just said the controller shouldn't know or care about that. Isn't that going right in the face of the 'loose coupling' principle that we had just set out? What's more, generating this information dynamically requires several operations: First, the controller has to look it up in the config file, then the controller has to package it in the $data array and pass it to the view, then the view has to extract the single variables $base and $css and look up their values.

Seems like a roundabout way of doing things. Why not just embed the data statically in the view?

<link rel="stylesheet" type="text/css" href="http://www.mysite.com/mystylesheet.css";">

The advantage of building this variable dynamically, despite breaking the MVC 'rule', and despite the overhead of creating variables and passing them around, is that the code is then much more portable. If you move the site, or move your CSS file, you only have to change the code once in the config file, and every controller and view will reflect the change at once. If you hard-code the address in to each view, you'll have to spend time hunting through them for all those absolute URIs you wrote months ago. So which is best?

There isn't a right answer. It depends on what your priorities are. The key is to apply MVC principles sensibly as a tool rather than a straitjacket. CI allows you a lot of freedom to do this.

A third option which I use is to create a special 'display' model. This exists to build the standard page. It puts in headers and CSS file references and so on, and it accepts as a parameter the unique information the file will require. I'll show you this later on in this chapter.

CI's Form Helper: Entering Data

Let's move on to how you use your HTML pages. One of the most important parts of any dynamic site is interaction with the users, and this usually means HTML forms. Writing forms is repetitive and boring.

The CI form helper is a very useful piece of code. It introduces a slightly different syntax, which makes form creation easier. Let's build a form to allow ourselves to enter data on our site about a new website. In a sites table, we want to enter the name, type, and URL of the website, and the date it was updated.

You can build the form as a simple HTML file, or you can build it inside a controller, pack it into a variable, then call a view, and pass the variable to the view. I'm doing the second.

Firstly, we have to load the form helper into the controller where we need to use it. Then, we put the following line in the controller's constructor function:

$this->load->helper('form'),

and of course, we have to start the form.

Now, for the form fields, instead of typing:

$variable .= <input type='text' name='name' value=''>

CI allows you to enter:

$variable .= form_input('name', ''),

(Remember that'name' is the title of the field,'value' is what you want to go into it. Putting something in here gives you a default value, or you can dynamically fetch the existing value from the table.)

Hmm, you say. 33 characters instead of 48, not much of a saving, particularly as I have to load the helper first (another 28 characters). Why bother? Well:

Form Helper Advantage One: Clarity

The first advantage of using the CI form helper is sheer clarity in your code. If you want a more elaborate input box, then in HTML you'd type:

$variable = 'input type="text" name="url" id="url" value="www.mysite.com" maxlength="100" size="50" style="yellow" />';

(Remember that type is the sort of box you want text, hidden, whatever.

name is the name that will be used as the key for this value in the POST array.

id is so that the box can be referenced on the page, if you're doing neat things with JavaScript.

value is the existing or default value that you want the box to show when it comes up on the page.

maxlength and size are obvious; style can be a set of HTML formatting or a reference to a .css style defined in your stylesheet.)

CI uses an array instead:

$data = array( 'name' => 'url', 'id' => 'url', 'value' => 'www.mysite.com', 'maxlength' => '100', 'size' => '50', 'style' => 'yellow', ); $variable = form_input($data);

It looks longer, and actually it is: 145 characters as opposed to 110 for the simple HTML. However, it is much clearer, and easier to understand and maintain. This becomes even more obvious if you start to generate some of the values dynamically.

Hidden form fields are very simple. Let's say we want to automatically record the date that our database was updated. We put the date in a $date variable, then:

form_hidden('updated', $date);

If you want a 'text area' box, to give your user more than one line to enter data, say for URLs, which may be quite long, use CI's form_textarea() function. If you're happy with a default size, this would simply read:

$data = array( 'name' => 'url', 'id' => 'url', 'value' => 'www.mysite.com', ); $variable = form_textarea($data);

CI's form helper is a great help when you write dropdowns and checkboxes or radio boxes. Let's say we want to change our'url' field to a drop-down box, to allow the reader to select one URL from a list of several. First, list the options in an array, then use the form_dropdown() function:

$urlarray = array( '1' => 'www.this.com', '2' => 'www.that.com', '3' => 'www.theother.com', ); $variable = form_dropdown('url', $urlarray, '1'),

The first value passed to the form, url, is the field name in the site table which we intend to update; the second is the array of options, the third is the key of the option you want to set as default. In other words, if the user accepts the default value, your $_POST array will contain the value'url => 1', but your user will see the option'www.this.com'.

Compare this to the plain vanilla HTML, you would otherwise have to write:

<select name="type"> <option value="1" selected>www.this.com</option> <option value="2">www.that.com</option> <option value="3" >www.theother.com</option> </select>

Now CI's code is actually shorter (128 characters as opposed to 154), as well as much easier to follow.

If you store your list of possible URLs in a separate database table (say it's called'urls'), then generating a dynamic drop-down box is easy. First generate an array of all possible values:

$urlarray = array();
$this->db->select('id, url'),
$query = $this->db->get('urls'),
if ($query->num_rows() > 0)
{
foreach ($query->result() as $row)
{
$urlarray[$row->id] = $row->url;
}
}

then repeat the CI form_dropdown() function we used before:

echo form_dropdown('type', $urlarray, '1'),

Only the contents of $urlarray have changed; this line of code remains the same.

If you are updating an entry rather than creating a new one, you don't want to show your user a default value. You want to show the already existing value for that entry. You should already know the id number of the entry you want to update, so you need to do a database lookup of the'sites' file first. Make sure you use a different variable name for the second query and the second 'row' variable, or they may overwrite the first set you wrote:

$this->db->select('id, url, name'),
$this->db->where('id','$id')
$sitequery = $this->db->get('sites'),
$siterow = $sitequery->row();

Then your CI form drop-down function will read:

echo form_dropdown('url', $urlarray, $siterow->url);

There isn't room here to go through all the options that the form helper offers. It can handle checkboxes, hidden fields, radio boxes, and others. It is fully explained in CI's online User Guide.

Form Helper Advantage Two: Automation

The second advantage of using the form helper to create your HTML forms is that it automates some things you would otherwise have to script yourself.

Firstly, it intercepts HTML and characters such as quotes, which the user may enter, and escapes them to stop them from breaking the form.

Secondly, it automates links. When you open a form, you have to specify the target page, which will receive the form data and process it. (In CI, this is a function within a controller rather than an actual static page. Let's say the update function of the websites controller.) So, if you were using plain HTML code, you'd write:

<form method="post" action="http:/www.mysite.com/index.php/websites/update" />

Whereas, if you open your form with CI, you only need to use:

form_open(websites/update)

CI automatically works out the base URL from the value in your config file and points the form there. Once again, if you move your site, you won't find that your forms are broken, and have to hunt through for hard-coded URLs to update.

Note incidentally, that CI assumes your forms will always send POST data rather than GET data. CI makes extensive use of the URLs itself, so this avoids confusion.

My 'Display' Model

As promised (and slightly simplified) here is my display model:

<?php
class Display extends Model {
/*create the array to pass to the views*/
var $data = array();
/*two other class variables*/
var $base;
var $status = '';
/*the constructor function: this calls the 'model' parent class, loads other CI libraries and helpers it requires, and dynamically sets variables*/
function Display()
{
parent::Model();
$this->load->helper('form'),
$this->load->library('user_agent'),
$this->load->library('errors'),
$this->load->library('menu'),
$this->load->library('session'),
/*now set the standard parts of the array*/
$this->data['css'] = $this->config->item('css'),
$this->data['base'] = $this->config->item('base_url'),
$this->base = $this->config->item('base_url'),
$this->data['myrobots'] = '<meta name="robots" content="noindex,nofollow">';
/*note that CI's session stuff doesn't automatically recall the extra variables you have added, so you have to look up the user's status in the ci_sessions table*/
$sessionid = $this->session->userdata('session_id'),
$this->db->select('status'),
$this->db->where('session_id', $sessionid);
$query = $this->db->get('ci_sessions'),
if ($query->num_rows() > 0)
{
$row = $query->row();
$this->status = $row->status;
}
diaplay modelabout}
/*function to assemble a standard page. Any controller can call this. Just supply as $mydata an array, of key/value pairs for the contents you want the view to display. Available variables in this view are:
mytitle. menu, mytext, diagnostic
*/
function mainpage($mydata)
{
$this->data['mytitle'] = 'Monitoring website';
$this->data['diagnostic'] = $diagnostic;
foreach($mydata as $key => $variable)
{$this->data[$key] = $variable;}
/*here's the menu class we looked at in Chapter 3*/
$fred = new menu;
$this->load->library('session'),
$mysess = $this->session->userdata('session_id'),
if(isset($this->status) && $this->status > 0)
{$this->data['menu']= $fred->show_menu($this->status);}
$this->load->view('basic_view', $this->data);
}
}
?>

I can call the main page from any controller with the lines:

$this->load->model('display'),
$this->display->mainpage($data);

and I then know that my view is being assembled dynamically, exactly as I want it.

CI's Validation Class: Checking Data Easily

One chore when you write HTML forms is validating user input. We all know that we should do it, but… So far we've written a simple form, which will trustingly accept any data the user enters. You should always assume that a small minority of your users are malicious, and all the others are stupid. (Just don't tell them.) If they can make a simple mistake, they will. Validating your form tests the information they submit, to make sure it fits rules you specify.

You can do it on the client side, using JavaScript but this is of limited value as a security precaution, as the user can easily circumvent it. Validating the data on the server side means an extra round trip to the server, but it's worth it for the added peace of mind.

It's also quite complex to write the code, but you guessed it CI has a validation class that works hand-in-glove with the forms helper to make validation easier.

Let's change our own form submission process to reflect this. You need to make some changes in the form, and also in the function to which it points.

If your form begins with form_open('sites/update'), the function you need to modify is the'update' function in the'sites' controller. If you aren't using the CI form helper, the HTML equivalent is:

<form method="post" action="http:/www.mysite.com/index.php/sites/update" />

You need to do three things:

  1. Set up validation

  2. Set up the controller

  3. Set up the forms

Set Up Validation

In the function to which your form points, load the validation library and declare your validation rules:

$this->load->library('validation'),
$rules['url'] = "required";
$rules['name'] = "required";
$this->validation->set_rules($rules);

Each entry must have something in the'url' and'name' fields. CI gives us various options for specifying what that something should be, and the User Guide explains them in full. They're fairly self-evident: min_length[6] obviously means a valid entry in the field must have 6 characters or more, numeric means it must not contain letters, etc. You can also combine rules, using the 'pipe' character to separate them:

$rules['name'] = "required|alpha|max_length[12]";

would require a name of 12 alphabetical characters or less. You can even write your own rules.

Set Up the Controller

Still in the same function, create an 'if/else' loop:

if ($this->validation->run() == FALSE)
{
$this->load->view('myform'),
}
else
{
$this->load->view('success'),
}

You run the validation test, and if the entries don't pass the test, you go back to the entry form. (If you're generating your view in a function inside a controller, say because it has dynamic drop-down fields, then point back to that function instead: $this->myfunction rather than $this->load->view('myform');

If your validation run is successful, create another view ("success") to tell the user that the entry has been accepted, and to give whatever navigation options you want him or her to have to move on.

Set Up the Forms

The entry form that we have built up also needs to be tweaked. Simply returning the user to the form each time an entry doesn't pass the validation tests will make your users really cross! You have to say which field failed, and why. So you have to echo out an extra line somewhere in the form:

$this->validation->error_string;

This prints out the appropriate messages, and saves the user a lot of frustration.

You also need to arrange for the form fields that were correctly entered to be re-populated with the correct values otherwise, the user will have to re-enter all the fields each time she or he makes a mistake in one of them. Another way to get her or him really cross!

Firstly, you need to go back to the controller and add some more code. Immediately after the validation rules you set up, create an array of each field you want to re-populate. The array key is the field name as actually used in your table; the array value is what you want the field to be called in the error message:

$fields['url'] = 'The URL of your site';

Afterwards, add the line:

$this->validation->set_fields($fields);

Now you've set up an array in the controller to store the existing values, you only need to add lines in your form to echo them back to the user. For a simple text line, this would be:

<input type="text" name="url" value="<?php echo $this->validation ->url; ?>" />

or, if you're using the CI form helper:

$variable .= form_input('url', "$this->validation->url");

If this is a new entry form, that should be enough. If you are using the form to update an existing entry, then, the first time the form appears, its value should be whatever it was set to beforehand. (Remember the code example above, where we looked that up and called it $siterow->url?)

But say you are using your form to update an existing entry, and it bounces back because one field doesn't validate, you want the value of every other field in your form to be whatever you had changed it to. Otherwise, you'd have to retype the whole form because of one validation error.

This is easily accomplished with an 'either/or' loop. Something like:

if(isset($_POST['url']))
{$myvalue = $this->validation->url;}
else{$myvalue = $siterow->url;}

The first time the form appears, there will be nothing in the post array; so you take values from the underlying table. But once you submit it, the post array will be populated, so you take the values from the validation function.

See the CI User Guide for several other things you can do with form validation. You can use it to:

  • Automatically prepare your data, e.g., by trimming it or removing potential cross-site scripting attacks

  • Write your own complex validation criteria, e.g., that the value the user has entered must not already exist in your database

  • Write your own error messages

The validation class is a very useful and powerful part of CI and well worth the time it takes to understand it.

Summary

We've looked at ways in which CI generates 'views', and how it allows you to create 'mini-views', which you can nest inside other views. This means that you can set up a title page, or a part of your display, once, and then use it over and over again, keeping your display separated from your content.

We've also seen how CI helps you through the chore of writing HTML forms, with a set of helpers that simplify the process and cut down on actual coding.

Lastly, we've looked at CI's validation class, which is a powerful tool for keeping an eye on what your users actually try to enter. Nothing's perfect, but this goes a long way towards stopping users form entering rubbish, or trying to exploit security holes in your site. It also looks much more professional when your site politely but firmly catches out user mistakes, rather than silently accepting meaningless entries.

On the way, we've also looked at the MVC process again, and made a choice between the strict application of MVC principles, deliberately breaking these 'rules' to make life easier. CI has a very flexible philosophy: use the tools if you want to, but provided you understand the issues feel free to do it some other way if that suits your priorities better.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.35.81