codeigniter_doctrineIn this episode we are going to build a User Signup Form following these steps:

- We build a “User” Model with Doctrine.
- Learn about Mutators.
- Create tables using Doctrine.
- Remove index.php from CodeIgniter urls
- Build and Style the Signup Form.
- Learn about Helpers, Libraries and how to extend them.
- Form Validation and checking for duplicates.

ci_doctrine_day3_3

"CodeIgniter and Doctrine from Scratch" Series:

download_code

Let’s build a Doctrine Model

Since we are building a user signup form today, it is best to start with a “User” Model.

Our model will be extending the Doctrine_Record class, which will represent records in a table named “user”.

So let’s make one:

  • Create a file named user.php under system/application/models
  • We name the class User, matching the file name.
    The class name is capitalized, but the filename is always lowercase. (This is a CodeIgniter rule, rather than Doctrine, but we’re still going go with it for consistency.)
  • We add a method named setTableDefinition(), which will contain column info.
  • We also add a method named setUp(), which will contain additional options.

system/application/models/user.php

<?php
class User extends Doctrine_Record {

	public function setTableDefinition() {
		$this->hasColumn('username', 'string', 255, array('unique' => 'true'));
		$this->hasColumn('password', 'string', 255);
		$this->hasColumn('email', 'string', 255, array('unique' => 'true'));
	}

	public function setUp() {
		$this->setTableName('user');
		$this->actAs('Timestampable');
	}
}

Looks simple right? Now let’s go over the code.

$this->hasColumn()

Structure:

$this->hasColumn($name, $type = null, $length = null, $options = array());

This function call tells Doctrine about the table column structure. Everything except the $name field is optional.

Supported $type‘s are: Boolean, Integer, Float, Decimal, String, Array, Object, Blob, Clob, Timestamp, Time, Date, Enum, Gzip.

$options are passed in an array, as ‘name’ => ‘value’ pairs. We passed an option named unique for 2 columns, which means we will have unique indexes on them.

We don’t need to get into more detail on this, but if you want to read more, read Doctrine docs.

$this->setTableName()

Structure:

$this->setTableName($table_name);

Simply sets the table name the Model will use. This can have a totally different name than the Model.

$this->actAs()

Adds a behavior to the model.

The one we used was Timestampable. It adds 2 fields to the table and manages them. They are called created_at and updated_at. And you guessed it, they keep track of the records create and update times.

Note: When we add this behavior, we don’t need those 2 columns specified inside the setTableDefinition() function.

Adding Mutators

Let’s say we want our password field to be automatically encrypted for security. This is where Mutators come in.

So, make the following changes to our model.

system/application/models/user.php (new code is highlighted)

<?php
class User extends Doctrine_Record {

	public function setTableDefinition() {
		$this->hasColumn('username', 'string', 255, array('unique' => 'true'));
		$this->hasColumn('password', 'string', 255);
		$this->hasColumn('email', 'string', 255, array('unique' => 'true'));

	}

	public function setUp() {
		$this->setTableName('user');
		$this->actAs('Timestampable');
		$this->hasMutator('password', '_encrypt_password');
	}

	protected function _encrypt_password($value) {
		$salt = '#*seCrEt!@-*%';
		$this->_set('password', md5($salt . $value));
	}
}

First we added a Mutator function named _encrypt_password to the password column.

Then we implemented it as a method inside our Model class.

Note: For mutators to work, auto_accessor_override option needs to be enabled. We have already done it, in our plugin file doctrine_pi.php.

Let Doctrine create the tables

Why create the user table manually ourselves when we can just let Doctrine do it for us. :)

We will use the Doctrine static class to create our tables like this:

Doctrine::createTablesFromModels();

So let’s create a simple and reusable script that we can rebuild our database with.

  • Create this controller: system/application/controllers/doctrine_tools.php
<?php
class Doctrine_Tools extends Controller {

	function create_tables() {
		echo 'Reminder: Make sure the tables do not exist already.<br />
		<form action="" method="POST">
		<input type="submit" name="action" value="Create Tables"><br /><br />';

		if ($this->input->post('action')) {
			Doctrine::createTablesFromModels();
			echo "Done!";
		}
	}

}

Only pay attention to the highlighted line for now. It simply reads our Doctrine Model class files, and creates tables for them in the database.

I know the code is a bit ugly, but we’re just going to use this controller temporarily as we write our initial models.

ci_doctrine_day3_1

Note: The id column got generated automatically. We already have a setting in our doctrine_pi.php plugin file:

// set the default primary key to be named 'id', integer, 4 bytes
Doctrine_Manager::getInstance()->setAttribute(
	Doctrine::ATTR_DEFAULT_IDENTIFIER_OPTIONS,
	array('name' => 'id', 'type' => 'integer', 'length' => 4));

Now let’s do some more CodeIgniter tweaking.

Removing “index.php” from CodeIgniter urls

We would like to have url’s like this:


http://localhost/ci_doctrine/welcome

instead of this:


http://localhost/ci_doctrine/index.php/welcome

Shorter, cleaner and nicer.

follow these steps:

  • Our web server needs mod_rewrite enabled.
    For WAMP users: Left-Click the system tray icon -> Apache -> Apache Modules -> rewrite_module
  • Create a file named .htaccess under our project folder “ci_doctrine”.
  • Copy-paste the following:

.htaccess:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ /ci_doctrine/index.php/$1 [L]
</IfModule>
<IfModule !mod_rewrite.c>
    ErrorDocument 404 /ci_doctrine/index.php
</IfModule>

Now we need to tell CodeIgniter that we’re removing index.php from our url’s.

  • Edit: system/application/config.php
/*
|--------------------------------------------------------------------------
| Index File
|--------------------------------------------------------------------------
|
| Typically this will be your index.php file, unless you've renamed it to
| something else. If you are using mod_rewrite to remove the page set this
| variable so that it is blank.
|
*/
$config['index_page'] = "";

That’s it!

To make sure it’s working, visit: http://localhost/ci_doctrine/welcome.
You should see the welcome page.

Building the Signup Form

Before we start coding, let’s decide how we’re going to do this

The Game plan

We want a Controller named signup, so the url for the signup form becomes http://localhost/ci_doctrine/signup
We would like this signup controller to load a View named signup_form by default, which will contain the html for the form.
The form will submit to a method named submit inside the signup controller i.e. signup/submit.
From there we need to validate the user input. Display errors if needed.
Finally save the new user to the database using our new Doctrine Model.

“Signup” Controller

  • Create this file: system/application/controllers/signup.php
<?php

class Signup extends Controller {

	public function __construct() {
		parent::Controller();
	}

	public function index() {
		$this->load->view('signup_form');
	}

}

Now let’s go over the code.

  • First we created a constructor method (line 5). This will get called first every time.
    Constructors are optional, however there is a reason I decided to create one, which we will see in a bit.
  • In the constructor, we always must call the parent constructor first (line 6).
  • In the index() method, (which acts as the default method for the controller), we load a View named signup_form.

“signup_form” View

  • Create this file: system/application/views/signup_form.php
<!DOCTYPE html>
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>Signup Form</title>
</head>
<body>

<div id="signup_form">

	<p class="heading">New User Signup</p>

</div>

</body>
</html>

For now it’s just a simple HTML file.

You should see the output:

New User Signup

CodeIgniter Helpers

Helpers are located in system/helpers. They contain procedural utility functions that make our lives easier.

In this tutorial, we will be using the url and form Helpers.

They can be loaded like this:

$this->load->helper('url');

Or load multiple helpers at once:

$this->load->helper(array('form','url'));

CodeIgniter Libraries

Libraries are very similar to Helpers, but they tend to be Object Oriented instead.

We will be using the form_validation library, and load it like this:

$this->load->library('form_validation');

Once it’s loaded, we can access it like this:

$this->form_validation->//...

Add them to the controller

  • Edit: system/application/controllers/signup.php
<?php

class Signup extends Controller {

	public function __construct() {
		parent::Controller();

		$this->load->helper(array('form','url'));
		$this->load->library('form_validation');
	}

	public function index() {
		$this->load->view('signup_form');
	}

}

Note: When you load Helpers and Libraries inside the Constructor, they become available in all methods of that Controller. For example, now index() function can use them.
Note2: When Views are loaded, they also can use the Helpers and Libraries that the Controller had loaded.

Form Helper

Now that we have it loaded, let’s use our Form Helper functions.

  • Edit: system/application/views/signup_form.php
<!DOCTYPE html>
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>Signup Form</title>
</head>
<body>

<div id="signup_form">

	<p class="heading">New User Signup</p>

	<?php echo form_open('signup/submit'); ?>

	<p>
		<label for="username">Username: </label>
		<?php echo form_input('username'); ?>
	</p>
	<p>
		<label for="password">Password: </label>
		<?php echo form_password('password'); ?>
	</p>
	<p>
		<label for="passconf">Confirm Password: </label>
		<?php echo form_password('passconf'); ?>
	</p>
	<p>
		<label for="email">E-mail: </label>
		<?php echo form_input('email'); ?>
	</p>
	<p>
		<?php echo form_submit('submit','Create my account'); ?>
	</p>

	<?php echo form_close(); ?>
</div>

</body>
</html>

I highlighted all the different kinds of functions we just called. These functions create the html for our form tags and elements.

ci_doctrine_day3_2

And in the source code you can see:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>Signup Form</title>
</head>
<body>

<div id="signup_form">

	<p class="heading">New User Signup</p>

	<form action="http://localhost/ci_doctrine/signup/submit" method="post">

	<p>
		<label for="username">Username: </label>
		<input type="text" name="username" value=""  />	</p>
	<p>
		<label for="password">Password: </label>
		<input type="password" name="password" value=""  />	</p>
	<p>

		<label for="passconf">Confirm Password: </label>
		<input type="password" name="passconf" value=""  />	</p>
	<p>
		<label for="email">E-mail: </label>
		<input type="text" name="email" value=""  />	</p>
	<p>
		<input type="submit" name="submit" value="Create my account"  />	</p>
	</form>

</div>

</body>
</html>

Let's give it Style

  • Create a folder named css directly under our project folder ci_doctrine
  • Create file: css/style.css
body {
	font-family: "Trebuchet MS",Arial;
	font-size: 14px;
	background-color: #212426;
	color: #11151E;
}

input, textarea, select {
	font-family:inherit;
	font-size:inherit;
	font-weight:inherit;
}

#signup_form {
	margin-left: auto;
	margin-right: auto;
	width: 360px;

	font-size: 16px;

}

#signup_form .heading {
	text-align: center;
	font-size: 22px;
	font-weight: bold;
	color: #B9AA81;
}

#signup_form form {

	background-color: #B9AA81;
	padding: 10px;

	border-radius: 8px;
	-moz-border-radius: 8px;
	-webkit-border-radius: 8px;

}

#signup_form form label {
	font-weight: bold;
}

#signup_form form input[type=text],input[type=password] {
	width: 316px;
	font-weight: bold;
	padding: 8px;

	border: 1px solid #FFF;
	border-radius: 4px;
	-moz-border-radius: 4px;
	-webkit-border-radius: 4px;
}

#signup_form form input[type=submit] {
	display: block;
	margin: auto;
	width: 200px;
	font-size: 18px;
	background-color: #FFF;
	border: 1px solid #BBB;
}

#signup_form form input[type=submit]:hover {
	border-color: #000;
}

#signup_form .error {
	font-size: 13px;
	color: #690C07;
	font-style: italic;
}

This tutorial is not about CSS, so there is not much to explain here.

  • Edit: system/application/views/signup_form.php again.
  • Add the highlighted lines:
<!DOCTYPE html>
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>Signup Form</title>
	<link rel="stylesheet" href="<?php echo base_url(); ?>css/style.css"
		type="text/css" media="all">
</head>
<body>

As you can see, we just called the base_url() function, which returns http://localhost/ci_doctrine/ in this case. This function is part of the URL Helper.

ci_doctrine_day3_3

Form Validation

If you press the submit button now, you will receive a 404 error:

ci_doctrine_day3_4

Because our submit() method does not exist.

  • Edit: system/application/controllers/signup.php
<?php

class Signup extends Controller {

	public function __construct() {
		parent::Controller();

		$this->load->helper(array('form','url'));
		$this->load->library('form_validation');
	}

	public function index() {
		$this->load->view('signup_form');
	}

	public function submit() {

		if ($this->_submit_validate() === FALSE) {
			$this->index();
			return;
		}

		$this->load->view('submit_success');

	}

	private function _submit_validate() {

		// validation rules
		$this->form_validation->set_rules('username', 'Username',
			'required|alpha_numeric|min_length[6]|max_length[12]');

		$this->form_validation->set_rules('password', 'Password',
			'required|min_length[6]|max_length[12]');

		$this->form_validation->set_rules('passconf', 'Confirm Password',
			'required|matches[password]');

		$this->form_validation->set_rules('email', 'E-mail',
			'required|valid_email');

		return $this->form_validation->run();

	}
}

Let's go over all the code we just added:

  • We add 2 methods: submit() and _submit_validate().
  • submit() get's called when the form is submitted to signup/submit.
    First it validates the input. If it fails, it calls the index() method, which display the Signup Form again.
  • If no errors found, we load a View named submit_success, which we will create in a bit.
  • _submit_validate() is just a private function we created, that contains the form validation stuff.
    And it returns the result of the Validation (true or false).

Let's look at how the form validation functions work:

When we loaded the Form_Validation Library earlier

First we set the rules like this:

$this->form_validation->set_rules('username', 'Username',
	'required|alpha_numeric|min_length[6]|max_length[12]');

The first parameter is the name of the form field.
The second parameter is the literal name for it, for display purposes.
The third parameter is a list of validation rules, separated by the pipe character "|".

You can see a list of validation rules here.

Finally we the run the validation by calling run() (line 42), which will return FALSE if a user input does not validate.

Displaying Validation Errors

Next thing we need to do is display the errors to the user.

  • Edit: system/application/views/signup_form.php
<!DOCTYPE html>
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>Signup Form</title>
	<link rel="stylesheet" href="<?php echo base_url(); ?>css/style.css"
		type="text/css" media="all">
</head>
<body>

<div id="signup_form">

	<p class="heading">New User Signup</p>

	<?php echo form_open('signup/submit'); ?>

	<?php echo validation_errors('<p class="error">','</p>'); ?>

	<p>
		<label for="username">Username: </label>
		<?php echo form_input('username',set_value('username')); ?>
	</p>
	<p>
		<label for="password">Password: </label>
		<?php echo form_password('password'); ?>
	</p>
	<p>
		<label for="passconf">Confirm Password: </label>
		<?php echo form_password('passconf'); ?>
	</p>
	<p>
		<label for="email">E-mail: </label>
		<?php echo form_input('email',set_value('email')); ?>
	</p>
	<p>
		<?php echo form_submit('submit','Create my account'); ?>
	</p>
	<?php echo form_close(); ?>

</div>

</body>
</html>

validation_errors() displays a list of the errors returned from the form validation (line 17).

The first and second arguments we passed are the html codes to be used to enclose each error output.

Also, On lines 21 and 33 we now pass a second argument to the form_input() function call. This way, when the form is re-displayed to the user, it will be populated with the values he entered previously, so he doesn't have to start all over again.

Submit Success View

We will keep this one simple for now.

  • Create: system/application/views/submit_success.php
Success!

Test The Form

  • Enter some invalid data into the form and submit.

http://localhost/ci_doctrine/signup

ci_doctrine_day3_5

And if you enter everything correctly, you should see the Success! message.

Saving the User

Now that our form works, we're going save the new user to the database using our Doctrine Model.

  • Edit the submit() function under system/application/controllers/signup.php:
<?php

class Signup extends Controller {
	// ...

	public function submit() {

		if ($this->_submit_validate() === FALSE) {
			$this->index();
			return;
		}

		$u = new User();
		$u->username = $this->input->post('username');
		$u->password = $this->input->post('password');
		$u->email = $this->input->post('email');
		$u->save();

		$this->load->view('submit_success');

	}

	// ...
}

As you can see, all we do is create a User object, assign the parameters, and call save(), and Doctrine should save the new record to the database. It's that simple.

Note: We are using $this->input->post() to get the submitted form values. When working with CodeIgniter, we do NOT use superglobals such as $_POST directly. This provides added security benefits.

Note2: We do NOT use any SQL filtering such as mysql_real_escape_string() when assigning the user input to the Doctrine Model, because Doctrine will take care of filtering for us.

Test the Form

ci_doctrine_day3_6

It's working great. But we're not quite done yet.

One Little Problem

Try submitting the form again with the same username and e-mail, and you will see:

<br />
<b>Fatal error</b>:  Uncaught exception 'Doctrine_Connection_Mysql_Exception' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'testing' for key 'username'' in C:\wamp\www\ci_doctrine\system\application\plugins\doctrine\lib\Doctrine\Connection.php:1084
Stack trace:
#0 C:\wamp\www\ci_doctrine\system\application\plugins\doctrine\lib\Doctrine\Connection\Statement.php(253): Doctrine_Connection-&gt;rethrowException(Object(PDOException), Object(Doctrine_Connection_Statement))
#1 C:\wamp\www\ci_doctrine\system\application\plugins\doctrine\lib\Doctrine\Connection.php(1049): Doctrine_Connection_Statement-&gt;execute(Array)
#2 C:\wamp\www\ci_doctrine\system\application\plugins\doctrine\lib\Doctrine\Connection.php(693): Doctrine_Connection-&gt;exec('INSERT INTO use...', Array)
#3 C:\wamp\www\ci_doctrine\system\application\plugins\doctrine\lib\Doctrine\Connection\UnitOfWork.php(595): Doctrine_Connection-&gt;insert(Object(Doctrine_Table), Array)
#4 C:\wamp\www\ci_doctrine\system\application\plugins\doctrine\lib\Doctrine in <b>C:\wamp\www\ci_doctrine\system\application\plugins\doctrine\lib\Doctrine\Connection.php</b> on line <b>1084</b><br />

The problem is, we tried to insert a duplicate value into a unique table column.

Handling Duplicates

We could simply check for the existence of a duplicate with something like this:

$user_table = Doctrine::getTable('User');
if ($user_table->findOneByUsername($username)) {
	// ... username already exists!
}

This is the first time we are looking into fetching records using Doctrine.

In the first line, we get the table object for our User Model. The name we pass is the name of the Model, and NOT the name of the table. This is important to know, in case your model and tables are named differently.

Then we call a magic function named findOneBy*(). It is magic, because it can be called on any other property, like findOneByEmail(). You basically need to append the property name, (which can be in camelCase format).

We could just add this code to our _submit_validate() function, for both username and email fields, however that's not quite what I want to do.

Extending CodeIgniter Libraries

Since we're using Doctrine to save time and avoid code duplication, it's only fitting that we continue with the same idea here.

We might need to check for duplicate records for other Models or from other Controllers later on. So we're going to build a reusable form validation rule, by extending the Form Validation class.

This way, other forms will have access to the same functionality later on, without having to duplicate code.

  • Create: system/application/libraries/MY_Form_validation.php
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

class MY_Form_validation extends CI_Form_validation {

	function unique($value, $params)
	{
		$CI =& get_instance();

		$CI->form_validation->set_message('unique',
			'The %s is already being used.');

		list($model, $field) = explode(".", $params, 2);

		$find = "findOneBy".$field;

		if (Doctrine::getTable($model)->$find($value)) {
			return false;
		} else {
			return true;
		}

	}
}

Let's go over the code:

  • Line 3: The new class needs to have the prefix MY_, and extends the core class named CI_Form_validation.
  • Line 5: We are going to create a validation rule named unique. With this form_validation class, names of the methods match the names of the rules, due to how the parent class is setup.
  • Line 5: First argument $value is value of the form field input.
  • Line 5: Second argument $params is the parameter passed to the rule. Our rule will have this structure: unique[model.field]. You will see it in a bit.
  • Lines 7-9: We need to get the CodeIgniter super object, so that we can access the form_validation instance, and set the error message.
  • Lines 12+: extract from the model.field values, build the name of the findOneBy function, and check for existing records.

You can read more about creating libraries in the docs.

Using the new form validation rule

  • Edit 2 lines in: system/application/controllers/signup.php
<?php

class Signup extends Controller {

	// ...

	private function _submit_validate() {

		// validation rules
		$this->form_validation->set_rules('username', 'Username',
			'required|alpha_numeric|min_length[6]|max_length[12]|unique[User.username]');

		// ...

		$this->form_validation->set_rules('email', 'E-mail',
			'required|valid_email|unique[User.email]');

		// ...
	}
	// ...
}

As you can see, now we can use our new form validation rule named unique. We also provide the model name and the field name to this rule, in square brackets.

Test the form again

You will see 2 errors:

ci_doctrine_day3_7

Stay Tuned

This is it for today. We covered quite a few new subjects and I hope you enjoyed it.

In the next episode, we create a User Login system.

See you next time!

"CodeIgniter and Doctrine from Scratch" Series: