Web Development
Using PHP

Follow on
Twitter

Introduction to the MVC pattern

Read up on the Model View Controller (MVC) pattern. See also the Data Access Layer (DAL) and Data Access Object (DAO).

Model

This file is used by other files to include all the model and DAO components. Right now it is simple, but if there were a lot of different types of data objects, it would allow us to easily modify what files are used in the model and DAL, while only requiring the code that uses it to include this one file.

<?php
// This file includes all the model files,
// and all the DAO files.
include 'person.php';

include 'db_setup.php';
include 'person_dao.php';
?>

Standard database setup file, nothing new here.

<?php
	// Information for hooking up to the database.
	$username = "cis305";
	$password = "cis305";
	$database = "cis305";
	$server   = "localhost";
?>

This uses a PHP class to represent a person object. Read up on PHP classes.

<?php
// This is a simple class that represents a user.
Class Person {
	
	// Public attributes
	public $first;
	public $last;
	public $id;
}
?>

Data Access Layer

All SQL code to access the person table goes here. Create additional files for accessing other tables.

This line of code uses code in a way that may be new to the reader:

while ($row = mysql_fetch_assoc($result)) {

Note this portion of the line:

$row = mysql_fetch_assoc($result)

Each time this code is run it fetches the next available row out of the result set from the SQL statement. If no result is available at all, it returns nothing.

The while statement will take any value other than zero and nothing and consider it true. So if there is a value returned, the while statement is considered true, and it loops again. If no row is returned, the statement in the while loop is considered false, and the loop ends.

Another new item is contained this code:

$person->id = $row['id'];

When working with an instance of an object, a PHP program accesses the fields and functions by using the -> symbol. This is the same as C and C++. Many other languages use the period instead.

There are cases where the -> symbol is not used. If the class has not been instantiated, and the member method or attribute is static, then it is accessed using two colons. For example:

$people = PersonDAO::get();
<?php

include "db_setup.php";

// This is a Data Access Object. All SQL to save/create/edit user objects
// goes in this class.

Class PersonDAO {

	public static function get() {
		global $server, $username, $password, $database;

		// Pull all users from the database
		$connection = mysqli_connect($server, $username, $password, $database) or die("Unable to connect");

		$query="SELECT * FROM people";
		$result = mysqli_query($connection, $query) or die("Query failed");
		$people_array = array();
		while ($row = mysqli_fetch_assoc($result)) {
			$person = new Person();
			$person->id = $row['id'];
			$person->first = $row['first'];
			$person->last = $row['last'];
			array_push($people_array,$person);
		}
		mysqli_free_result($result);
        mysqli_close($connection);
		return $people_array;
	}

	// Get a user based on username and password. Returns a User object if successful,
	// otherwise returns nothing.
	public static function get_by_id($id) {
		global $server, $username, $password, $database;

		// Pull all users from the database
		$connection = mysqli_connect($server, $username, $password, $database) or die("Unable to connect");

		// To protect MySQL injection (more detail about MySQL injection)
		$id = mysql_real_escape_string($id);

		$sql="select * from people where id='$id'";
		$result=mysqli_query($connection, $sql);

		// Mysql_num_row is counting table row
		$count=mysqli_num_rows($result);

		// If result matched $myusername and $mypassword, table must be 1 row
		if($count==1){
			$person = new Person();
			$row = mysqli_fetch_assoc($result);
			$person->first=$row['first'];
			$person->last=$row['last'];
			$person->id=$id;
			mysqli_free_result($result);
	        mysqli_close($connection);

			return $person;
		}
		else {
			mysqli_free_result($result);
	        mysqli_close($connection);
			return;
		}
	}

	public static function add($person) {
		global $server, $username, $password, $database;

		// Pull all users from the database
		$connection = mysqli_connect($server, $username, $password, $database) or die("Unable to connect");

		// To protect MySQL injection
		$first = mysql_real_escape_string($person->first);
		$last = mysql_real_escape_string($person->last);

		$sql="insert into people (first,last) values ('$first','$last')";
		mysqli_query($connection, $sql) or die ("Error: $sql");

        mysqli_close($connection);
	}

	public static function delete($id) {
		global $server, $username, $password, $database;

		// Pull all users from the database
		$connection = mysqli_connect($server, $username, $password, $database) or die("Unable to connect");

		// To protect MySQL injection
		$id = mysql_real_escape_string($id);

		$sql="delete from people where id=$id";
		$result=mysqli_query($connection, $sql) or die( "Query failed");
        mysqli_close($connection);
	}

	public static function update($person) {
		global $server, $username, $password, $database;

		// Pull all users from the database
		$connection = mysqli_connect($server, $username, $password, $database) or die("Unable to connect");

		// To protect MySQL injection
		$id = mysql_real_escape_string($person->id);
		$first = mysql_real_escape_string($person->first);
		$last = mysql_real_escape_string($person->last);

		$sql="update people set first='$first', last='$last' where id='$id'";
		mysqli_query($connection, $sql) or die ("Error: $sql");
        mysqli_close($connection);
	}

}
?>

View

The view should not have any SQL code in it. It should call the model data access objects to get data.

This code uses the foreach loop to iterate through the array of person objects returned by the PersonDAO::get() function.

<!DOCTYPE html>

<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

  <title>Manage People</title>
</head>

<body>
  <h1>Manage people</h1>
  <p><a href="form.php">Add new person</a></p>
  <h2>List of people:</h2>
  <?php
	// Include common db setup code
	include ("model/model.php");

	$people = PersonDAO::get();
	foreach ($people as $person) {
		$id_safe=htmlentities($person->id);
		$first_safe=htmlentities($person->first);
		$last_safe=htmlentities($person->last);
		echo "[<a href='controller/delete_process.php?id=$id_safe'>delete</a>] ";
		echo "[<a href='controller/edit_process.php?id=$id_safe'>edit</a>] ";
		echo "$first_safe $last_safe <br /> \r\n";
	}
	$num = count($people);
	echo "<p>Total number: $num</p>";

  ?>
</body>
</html>

Not much is new with the form except that it goes to controller/form_process.php instead of form_process.php.

<!DOCTYPE html>

<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>Sample PHP</title>
</head>

<body>
  <h1>Form Test</h1><?php
  session_start();
  if(isset($_SESSION['messages'])) {
    $messages = $_SESSION['messages'];
    $form = $_SESSION['form'];
    unset($_SESSION['messages']);
    unset($_SESSION['form']);
  }
  if(!empty($messages)) {
          foreach ($messages as $message) {
              echo $message;
          }
  }
  ?>

  <form action="controller/form_process.php" method="get">
    <!-- Enter name -->
<?php
	if (isset($form['id'])) {
		echo "<input type='hidden' name='id' value='";
		echo htmlentities($form['id']);
		echo "'>";
	}
?>
    First name: <input type="text" name="firstname" <?php
    if( isset($form['firstname']) ) {
        echo 'value="';
        echo htmlentities ($form['firstname']);
        echo '"';
    }
    ?>/><br />
    Last name: <input type="text" name="lastname" <?php
    if( isset($form['lastname']) ) {
        echo 'value="';
        echo htmlentities ($form['lastname']);
        echo '"';
    }
    ?>/><br />
    <!-- Add submit button -->
    <input type="submit" value="Submit" />
  </form>
</body>
</html>

Controller

Nothing in the contoller layer should display anything. It should only contain PHP code that processes a request. After the request is processed, forward to a view php file.

<?php
include ("../model/model.php");
session_start();
 
/* Keep track if there is an error or not */
$error=FALSE;
 
/* Validation of first name */
if(empty($_REQUEST['id'])) {
    $form['id']="";
} else {
    /* Get the first name from the request */
    $form['id'] = $_REQUEST['id'];
    /* See if the first name matches our not-very-good filter */
    if (!preg_match("/^[0-9]{1,25}$/", $form['id'])) {
        /* No match, what is happening? */
        $error=TRUE;
        /* Create an array of messages to display the user */
        $messages['id']="<p class='errormsg'>System error, unable to edit record.</p>"; 
    } 
} 
/* Validation of first name */
if(empty($_REQUEST['firstname'])) {
    /* There was no first name, that's odd */
    $error=TRUE;
    /* Create an array of messages to display the user */
    $messages['firstname']="<p class='errormsg'>Error - Invalid First Name</p>";
} else {
    /* Get the first name from the request */
    $form['firstname'] = $_REQUEST['firstname'];
    /* See if the first name matches our not-very-good filter */
    if (!preg_match("/^[A-Za-z]{1,25}$/", $form['firstname'])) {
        /* No match, display an error */
        $error=TRUE;
        /* Create an array of messages to display the user */
        $messages['firstname']="<p class='errormsg'>Error - Invalid First Name</p>"; 
    } 
}
/* Validation of last name */
if(empty($_REQUEST['lastname'])) {
    /* There was no last name, that's odd */
    $error=TRUE;
    /* Create an array of messages to display the user */
    $messages['lastname']="<p class='errormsg'>Error - Invalid Last Name</p>";
} else {	
    /* Get the last name from the request */
    $form['lastname'] = $_REQUEST['lastname'];
    /* See if the first name matches our not-very-good filter */
    if (!preg_match("/^[A-Za-z]{1,25}$/", $form['lastname'])) {
        /* No match, display an error */
        $error=TRUE;
        /* Create an array of messages to display the user */
        $messages['lastname']="<p class='errormsg'>Error - Invalid Last Name</p>"; 
    }	
}
 
/* If there was an error, include the form again. Otherwise continue on. */
if($error==FALSE) {

	// Create a person object and have the DAO put it in the database
	$person = new Person();
	$person->id = $form['id'];
	$person->first = $form['firstname'];
	$person->last = $form['lastname'];
	
	if( $person->id == "" ) {
		PersonDAO::add($person);
	} else {
		PersonDAO::update($person);
	}

    header("Location: ../list_people.php");
} else {
    $_SESSION['messages'] = $messages;
    $_SESSION['form'] = $form;
    header("Location: ../form.php");
}

?>
<?php
include ("../model/model.php");
session_start();

/* Keep track if there is an error or not */
$error=FALSE;
 
/* Validation of first name */
if(empty($_REQUEST['id'])) {
    /* There was no first name, that's odd */
    $error=TRUE;
} else {
    /* Get the first name from the request */
    $form['id'] = $_REQUEST['id'];
    /* See if the first name matches our not-very-good filter */
    if (!preg_match("/^[0-9]{1,25}$/", $form['id'])) {
        /* No match, display an error */
        $error=TRUE;
    } 
}
 
/* If there was no error, delete the record.  */
if($error==FALSE) {
	
	/* DANGER DANGER DANGER 
	 * This example code is VERY DANGEROUS 
	 * because it is missing two items.
	 *
	 * 1) In this example, the user does not need to be logged in
	 * to get to this point. Anyone may delete the people in the
	 * database. If a search engine explores the web site, it will
	 * explore all the delete links and delete all the records in
	 * the database.
	 *
	 * 2) Even if there was a log-in, this code does not
	 * check to see if the person logged in has permission to delete
	 * this particular record. Remember that the id value can be
	 * changed and anyone could swap it for a different number.
	 */
	PersonDAO::delete($form['id']);
}
/* Go back to the list people page. */
header("Location: ../list_people.php");

?>
<?php
include ("../model/model.php");
session_start();
 
/* Keep track if there is an error or not */
$error=FALSE;
 
/* Validation of first name */
if(empty($_REQUEST['id'])) {
    /* There was no first name, that's odd */
    $error=TRUE;
} else {
    /* Get the first name from the request */
    $form['id'] = $_REQUEST['id'];
    /* See if the first name matches our not-very-good filter */
    if (!preg_match("/^[0-9]{1,25}$/", $form['id'])) {
        /* No match, display an error */
        $error=TRUE;
    } 
}
 
/* If there was no error, load the record.  */
if($error==FALSE) {
	/* Set up the database connection */
	include ("db_setup.php");
	
	/* DANGER DANGER DANGER 
	 * This example code is VERY DANGEROUS 
	 * because it is missing an important items.
	 *
	 * 1) If there was a log-in, this code does not
	 * check to see if the person logged in has permission to edit
	 * this particular record. Remember that the id value can be
	 * changed and anyone could swap it for a different number.
	 */
	
	/* Run the SQL statement */
	$person=PersonDAO::get_by_id($form['id']);
	
	/* Grab the data and put in the form array */
	$form['id']=$person->id;
	$form['firstname']=$person->first;
	$form['lastname']=$person->last;
	
	/* Save form to session */
    $_SESSION['form'] = $form;
	
	/* Send message to user */
	$messages['message']="Editing the record for " . $form['firstname'] . " " . $form['lastname'];
	
	/* Save messages to session */
    $_SESSION['messages'] = $messages;
	
	/* Go to the form */
	header("Location: ../form.php");
} else {
	/* Error, just go back */
	header("Location: ../list_people.php");
}
?>

Try it out: [link]

You are not logged in. Log in here and track your progress.