Roll Your Own PHP Framework: Part III

In Part I and Part II we looked at how to set up our file structure, how to get URL routing working and how to set up templating for our little framework. In this final part to the series, we are going to briefly look at database access.

Mini-series Overview

You can download all the files put together during this three part series here:

Download zipped archive

I’m not going into detail about how to create an ORM or ActiveRecord clone. We are just going to write a simple helper class to setup our connection and query. Also I’m going to assume you know how to create a database and table in the database.

I have created a database called “peanut” and created table:

users:
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| username | varchar(255) | NO   |     | NULL    |                |
| password | varchar(255) | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

In this example we are going to use Mysql and the php mysql_ methods. So break open system/database.php and drop the following in:

<?php

// Here is where we are setting up a simple wrapper class around mysql
// functions just to make it a little more convenient.  Our models will
// simple extend our database class and simplify making queries just a bit.
class Database
{
	protected var $connection;
	// Every time you instantiate this class you are going to create
	// a new connection to the database.
	public function __construct()
	{
		// First we setup up a nice little connection to the database,
		// make sure you use your connection information.  If the
		// connection fails we just die with the error.
		$this->connection = mysql_pconnect("localhost", "root", "somepassword") or die("MySQL Error: " . mysql_error());

		// And let's tell mysql which db we are going to use.
		mysql_select_db( "peanut", $this->connection );
	}

	// This is just a helper function to help out against
	// possible sql injection attacks.
	public function escapeString($string)
	{
		// we call mysql's 'cleaning' function on strings
		// just to make sure we get a little safer item to query
		// with.
		return mysql_real_escape_string($string);
	}

	// Here we will query the database with the passed query string,
	// build up an array of objects and return them, simple enough.
	public function query($qry)
	{
		// Here we make our query and set the result to a $result variable
		$result = mysql_query($qry) or die("MySQL Error: " . mysql_error());

		// Create a container array variable to hold all of our result objects.
		$resultObjects = array();

		// This might look weird, but all we are doing is saying,
		// While you are still getting results, please put the next
		// result into the next spot on my array.
		while($resultObjects[] = mysql_fetch_object($result));

		// Now we just return our array that has all our result objects in it
		return $resultObjects;
	}

	// Here we add a simplier method for handling INSERTs and UPDATEs since
	// they do not return a result set.
	public function execute($qry)
	{
		$exec = mysql_query($qry) or die("MySQL Error: " . mysql_error());
		return $exec;
	}
}
?>

So we have our database class in place and ready to use, now we just need to require it in the index.php like we did the others, so pop that bad boy open and after the template require, do one just like it but for the database.php

Now let’s crack open actions/helloworld/models.php and let’s write a simple addUser and getAllUsers method to it. We are going to make our model a class as well so that it can extend our Database class that we wrote.

Here are the guts:

<?php

// All of our database back-and-forth will be handled in our models file
// Don't be mistaken, this is no where close to an ORM (Object Relational Mapper)
// it's just simplified database access.

class myModel extends Database
{
	// When our model is instantiated we just need
	// to also instantiate our parent class (Database)
	// so it knows to make the connection to the database.
	public function __construct()
	{
		// We just call the __construct of Database class.
		parent::__construct();
	}

	// addUser will use the execute method of Database
	// since we are inserting a value and not expecting
	// anything in return.
	public function addUser($username, $password)
	{
		// Here we use that little cleaning method we
		// wrote to make our strings pretty and make sure
		// they will play nice with mysql
		$username = $this->escapeString($username);
		$password = $this->escapeString($password);

		// We execute our insert and if it worked $success
		// will be true else it will be false.
		$success = $this->execute("INSERT INTO users (username, password) VALUES ('{$username}','{$password}')");

		// We return $success to inform our page action that it has or hasn't worked.
		return $success;
	}

	// This method does just what you think it would.
	// We are going to use the query function because
	// we are expecting data back, then we are just
	// going to return the array of user objects.
	public function getAllUsers()
	{
		// Populate the $users variable with the results of our query
		$users = $this->query("SELECT * FROM users");

		// Now we return our results
		return $users;
	}
}
?>

Now we need to change our actions/helloworld/actions.php mypage page action to:

<?php
// We need to include our models file so we can access the database
require(PEANUT_ROOT_DIR . 'actions/helloworld/models.php');

// We simply define the function (the second item in our request url)
// making sure it is the same name as the one in the url.

function mypage()
{
	// Let's do some database work!
	// Here we are going to instantiate our model
	$model = new myModel();

	// Now let's add a user
	// On a funny note, if you keep refreshing it will add this
	// user over and over in your users table so your getAllUsers call
	// will result in a list that grows by one each time.
	$model->addUser("wess", "password");

	// Now let's fetch all of our users and put them in our users
	// variable.
	$users = $model->getAllUsers();

	// Let's create a new template object and pass it the path to our template
	$template = new Template("helloworld/templates/helloworld.php");

	// I bet you want to display a table in your template with all your newly
	// created users. So let's do it

	// That's write we use the same set command, and it will set our array of users
	// to our template variable $users, and since php is our template language
	// it's real easy to print them to the screen.
	$template->set('users', $users);

	// Set our page variable "title" with the value "Hello World"
	$template->set('title', 'Hello World');

	// Set our page variable "message" with our little message
	$template->set('message','This is my first message for my template');

	// Now we can call render and return it to dispatch to
	// display in our browser
	return $template->render();
}
?>

So now you are handing all your users from your database to the template, so let’s display them. Change your actions/helloworld/templates/helloworld.php file to:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
	<head>
		<meta http-equiv="Content-type" content="text/html; charset=utf-8">
		<title>My PEANUT Page</title>

	</head>
	<body>
		<h1><?php echo $title ?></h1>
		<p><?php echo $message ?></p>

		<h2>Users:</h2>
		<table>
			<thead>
				<tr>
					<th>ID</th>
					<th>Username</th>
					<th>Password</th>
				</tr>
			</thead>
			<tbody>
				<!--
					Here we are going to use native php foreach look
					to create each row of the table with our list of users.
					Notice how the foreach is done with a : and surrounding php tags.
				-->
				<?php foreach($users as $user): ?>
					<tr>
						<td><?php echo $user->id ?></td>
						<td><?php echo $user->username ?></td>
						<td><?php echo $user->password ?></td>
					</tr>
				<?php endforeach; ?>
			</tbody>
		</table>
	</body>
</html>

Now if all went well, you should save all your files and open up: http://mywebapp.local/helloworld/mypage and see all the users that you added in a table.

So you have created a template class, url basic url routing, simplified database access, and a page action. You have all the core basics of a PHP Framework at your hands now. This is just for a starting point or foundation of understanding, please do not use this in a production environment.

I have really enjoyed writing this and I hope you learned something from my rambling! The source code is available for download at the top of this article.

Take care,
Wess “Wattz”

Updated October 26, 2009: Thanks to Alex and Ian for spotting some errors in the code and supplying the fixes. The code examples have been updated to reflect the changes as has the ZIP file.

Since the days of the Commodore 128, Wess has become an expert in C, C++, Java, Javascript, Python, and PHP and he also has extensive knowledge of Objective-C/Cocoa, Ruby, and Erlang. His blog can be found at www.wattz.net.

 

If you liked this article, please help spread the news on the following sites: