In this series, we have been looking at the development of our own simple PHP MVC Framework. In order to add an authentication system and security to our framework, we need to be able to work with users. We could write our authentication and security tools, and test them using PHPUnit, without having created any users, but why don’t we have a break from the underlying framework tools and implement some user creation and management features for our app. We can utilise many of the elements we have already created and tested, and bring them together to demonstrate how our framework will help us build our app.

At this stage in our application development, we need to be able to create users, edit users, reset their passwords, and disable their access to our app. These are the features we will look at in this article. There are a few other things we will need to look at as well. Currently, we have not looked at displaying things in the browser, or collecting information from users. We have merely been looking at the behind the scenes components of our application.

In order to collect data, we will need to make use of HTML forms, and process the data using PHP. We could do this manually, and implement our own data validation tools, however there is a very good PHP forms library written by Stefan Gabos, called Zebra_Form. We will utilise this library in our application, as it allows us to quickly and easily create and handle forms. I have installed the library using composer, and the instructions for this can be found on the Zebra_Form website.

A few things worthy of noting when installing Zebra_Form for use with Google App Engine, is that we need to copy some of the resources to our public directory, so they are served correctly as static files. Remember when we set up the app.yaml file and we set the public directory as a static directory? This allows css files, images and javascript files to be served to our app properly. The files I have copied to this directory, are all of the files within the zebra_form/public/css directory, as well as zebra_form.js which is found in zebra_form/public/javascript. I have also added the latest version of jquery.js to our public directory. From here, installing Zebra_Form is as simple as linking and including some files in our app/index.php file, as shown below.

//	app/index.php -- additions only --

<html>
	<head>
		<title>A SaaS Experience</title>
		<link rel="stylesheet" href="/public/zebra_form.css">
		<script src="/public/jquery.js"></script>
		<script src="/public/zebra_form.js"></script>
	</head>
	
	<body>

Now that we have Zebra_Form installed, we can consider how we are going to work within our MVC structure, to handle users. The first thing we will need is a Model. Without a Model, we are unable to store or retrieve any data. We will need a User entity type for our Model, as well as a UserStore. Our User entity type is a simple extension of the GDS\Entity class, and our UserStore will be an extension of the CacheStore class we created in the https://buziit.com.au/performance-with-memcached-a-saas-experience/ article.

<?PHP	// models/user.class.php

class User extends GDSEntity {

}	
	
?>
	
<?PHP	// models/userstore.class.php
	
class UserStore extends CacheStore{
	
	private $User;
	
	public function buildSchema(){
		$Schema = new GDSSchema('User');
		$Schema->addString('Username', true);
		$Schema->addString('Password', false);
		$Schema->addString('Name', true);
		$Schema->addString('Surname',true);
		$Schema->addString('Email', true);
		$Schema->addString('CurrentCompany', true);
		$Schema->addBoolean('Active', true);
		return $Schema;
	}
	
	public function __construct($Gateway, $Cache){
		parent::__construct($this->buildSchema(), $Gateway, $Cache);
		$this->setEntityClass('User');
	}
	
?>

As you can see, our user.class.php file is very simple, and simply defines an entity type. Our UserStore class extends the CacheStore class, which automatically handles both caching and storing our User entities. So far, the class definition shows a buildSchema() method, similar to those we have seen previously when working with stores. In the schema we define the following properties for our users:

  • Username – string, indexed
  • Password – string, not indexed
  • Name – string, indexed
  • Surname – string, indexed
  • Email – string, indexed
  • CurrentCompany – string, indexed
  • Active – boolean, indexed

We may wish to add more user information later, however this will suit our current needs. Notice we have created a CurrentCompany string, this will be used to store the company the user is able to access, to assist us with our multi-tentant architecture. This will help us with name-spacing our data to ensure the user can only access data relevant to that company. We have also added an Active property, that we can set to true or false, to determine if the user is Active and can access the application.

The __construct() method is similar to the stores we have seen previously, accepting a Gateway object and a Cache object, connecting our store to the Datastore and Cache. We then call the CacheStore’s constructor and provide our schema, along with the Gateway and Cache objects. We then set the store’s entity to our new User entity type.

Our store is now a working Model, we can save and retrieve User objects, with the speed of memcached, and the reliability of Google’s Datastore. In reality though, our Model needs to be able to do a lot more. We need to know how to obtain user information, and how to process it into our cache and datastore. We can use our Model to generate the relevant forms required to capture and maintain our User data.

The first task, is how do we capture information to create a User? We can create a form that allows us to add and update User information.

<?PHP	// models/userstore.class.php -- additions only --
	
	public function getUpdateForm($New = false){
		$Form = new AppForm('EditUser');
		$Form->add('label', 'labelUsername', 'Username', 'Username: ');
		if($New){
			$Username = $Form->add('text', 'Username');
			$Username->set_rule(array(
				'required' => array('error','Username is required!'),
			));
		} else{
			if(isset($_POST['UserKey'])){
				$this->User = $this->fetchById($_POST['UserKey']);
				if(!$this->User){
					throw new UserException('Trying to update a user, but the UserKey provided returned no valid user');					
				} else{
					$Username = $Form->add('text','Username', $this->User->Username, array('disabled' => 'disabled'));
				}

			} else{
				throw new UserException('Trying to update a user, but no UserKey has been supplied. $_POST[UserKey] not set.');
			}
		}
		
		if($New){
			$Form->add('label', 'labelPassword','Password','Password: ');
			$Password = $Form->add('password','Password');
			$Password->set_rule(array(
				'required' => array('error','Password is required!'),
				'length' => array(8,0,'error','Password must be at least 8 characters long.'),
			));
			
			$Form->add('label','labelConfirmPassword','ConfirmPassword','Confirm Password: ');
			$ConfirmPassword = $Form->add('password','ConfirmPassword');
			$ConfirmPassword->set_rule(array(
				'compare' => array('Password','error','Passwords do not match!'),
			));
		}
		
		$UName = '';
		$USurname = '';
		$UEmail = '';
		
		if(isset($this->User)){
			$UName = $this->User->Name;
			$USurname = $this->User->Surname;
			$UEmail = $this->User->Email;
		}
		
		$Form->add('label','labelName','Name','Name: ');
		$Name = $Form->add('text','Name', $UName);
		$Name->set_rule(array(
			'required' => array('error','Your name is required.'),
		));
		
		$Form->add('label','labelSurname','Surname','Surname: ');
		$Surname = $Form->add('text','Surname', $USurname);
		$Surname->set_rule(array(
			'required' => array('error','Your surname is required.'),
		));
		
		$Form->add('label','labelEmail','Email','Email: ');
		$Email = $Form->add('text','Email', $UEmail);
		$Email->set_rule(array(
			'required' => array('error','Your email is required.'),
			'email' => array('error','Your email address is not valid'),
		));
		
		if(isset($this->User)){
			$Form->add('hidden','UserKey', $this->User->getKeyId());
		}
		
		$Form->add('submit','btnSubmit','Save User');
		
		$this->UpdateForm = $Form;
		return $Form;
	}
?>

Wow! This may look a little scary, but we will break it down. Our getUpdateForm() method will have two purposes. Firstly, to allow us to create a new user, and secondly, to allow us to update an existing user. It makes sense to combine both of these forms in one place, as the information we need does not change between the two forms, and we don’t want to have to change details in two places should we add or remove items from our User model. With this in mind, our getUpdateForm() method accepts a $New argument, which can be either true or false. If we set true, we are indicating we are adding a new user, if false, we are updating an existing user.

The first line creates a new Zebra_Form. I have extended the Zebra_Form class with a class named AppForm (shown below), simply to make the name easier to type in our code! We then add a label to our form, using the add() method, setting the item to ‘label’, the name to ‘labelUsername’, the control the label is intended for to ‘Username’ and finally what we want the label to say ‘Username: ‘. This will add a label to our form next to the Username field, saying Username:.

Next, if our $New value is true, we want to be able to specify a username, so we use the add() method on our Form object to add a text field called Username. We need our username to be a required field, so we can use Zebra_Form’s set_rule() method to make it a required field. Rules are set within an array, and in this case we want the ‘required’ rule. We then set an error message using an array specifying we wish to show an ‘error’, and provide a message ‘Username is required!’.

If our $New is set to false, we are using this form to update an existing user, in which case the username will already be set. If this is the case, we ensure we have access to the UserKey, which will be provided as a $_POST variable. If we have a User Key, we can use the fetchById() method we have inherited from our CacheStore class to retrieve our user. If we can’t find the user, we need to throw an exception, as we cannot continue. I have extended our BuziException class to create a UserException (shown below) class with both $Log and $Mail set to true. I want to be notified if my user management features are not working correctly. If we have found our user, we can add a text field to the form, and set the value to the username of the User we found. We also set the field’s attributes to disabled, so the value cannot be edited. If we did not have a UserKey available, we throw a UserException to notify ourselves that we have called for an update form, but not provided the UserKey.

Next, if our $New value is true, we need to set a password for our new User. We can do this by adding a label, and a password field, and also setting the field as a required field, and requiring a minimum length of 8 characters. The all of the available rules we can use to validate fields can be found in the Zebra_Form documentation, but you will encounter many of them throughout our app development. We can also set a field to confirm the entered password. We can do this by adding a new label and password field, and setting a rule to compare the values against the Password field.

In the next lines, we have set default empty strings for a few of our fields, then we test if we have user data available. If so, we can set the values of our fields to the user data we have retrieved. We then go ahead and set up text fields for the User’s Name, Surname and Email address, with all being required fields, and the Email field having a rule that states it must be an email. The Zebra_Form library will validate the email address for us when the form is submitted.

Next, we check to see if we have a UserKey available, and if so, we create a hidden field named UserKey, so that we can process the data we have retrieved. Finally, we add a submit button to our form, and return the form.

Now any time we need to update a user or create a new user, we can get the correct form by calling getUpdateForm().

<?PHP	// core/appform.class.php

class AppForm extends Zebra_Form{
	
}
	
?>
	
<?PHP	// exceptions/userexception.class.php

class UserException extends BuziException{
	protected $Log = true;
	protected $Mail = true;
}
	
?>

Our next task is to process the results of the form when it is submitted. A bit later, in our View, you will see we call a validate() method on the form, which makes the Zebra_Form library validate all of the data we have gathered, against the rules we have set. Only when the data is validated will our processUpdateForm() method be called. This means, we will not have to validate data when processing it.

<?PHP	// models/userstore.class.php -- additions only --
	
	public function processUpdateForm(){
		if(!isset($_POST['UserKey'])){
			$this->User = new User();
			$this->User->Username = $_POST['Username'];
			$this->User->Password = $this->generatePassword($_POST['Password']);
		} else{
			$this->User = $this->fetchById($_POST['UserKey']);
			if(!$this->User){
				throw new UserException('Trying to process user update form, but the UserKey provided returned no valid user');
			}
		}
		
		$this->User->Name = $_POST['Name'];
		$this->User->Surname = $_POST['Surname'];
		$this->User->Email = $_POST['Email'];
		
		$this->setCurrentCompany('ExampleCompany');
		$this->User->Active = true;
		
		$this->upsert($this->User);
		
		$this->User->setKeyId($this->findNewKeyId($this->User->Username));
		
		$_POST['UserKey'] = $this->User->getKeyId();
		
		$this->getUpdateForm(false);
		
		$this->UpdateForm->add_error('error', 'The user '.$this->User->Username.' has been updated.');
		return $this->UpdateForm;
			
	}

When we begin processing the UpdateForm, we check to see if we have a UserKey available. If we do, we are updating an existing user. If it is not available, we are creating a new user. To create a new user, we create a new instance of our User entity, and set the Username with the Username provided in the $_POST variable. We then set the password by generating a secure password hash (shown below) from the provided password. If we have a UserKey, we can retrieve the User data from the store using the fetchById() method, and if we don’t find a user, throw a UserException to notify us that something has gone wrong.

We can now set each of the properties we have gathered to our User entity, and then use the upsert() method to store the User entity. We then use a helper function (shown below) to find the newly created UserKey, and set it to our User entity. We then re-generate the update form, to load the changed data, we also add a message that the User has been updated, and then return the new update form.

<?PHP	// models/userstore.class.php -- additions only --
	
	private function generatePassword($Password){
		$Hash = password_hash($Password, PASSWORD_BCRYPT);
		return $Hash;
	}
	
	private function findNewKeyId($Username){
		$User = $this->fetchOne("Select * FROM User WHERE Username = @Username", ['Username' => $Username]);
		$UserKey = $User->getKeyId();
		return $UserKey;
	}

?>
	
<?PHP	// core/index.php -- additions only --
	include_once 'core/password.php';
?>
	

The password/php file we have included in index.php is an open source password hashing library by Anthony Ferrara, which can be found at https://github.com/ircmaxell/password_compat

Now that we have a model that is able to add a user to our application, we can create a Users controller to allow us to load an add View and load our Model.

<?PHP	// controllers/users.class.php

class Users extends Controller{
	
	public function __construct(){
		$this->Model(App::newUserStore());
	}
	
	private function getUpdateForm($New = false){
		$UpdateForm = $this->Model->getUpdateForm($New);
		return $UpdateForm;
	}
	
	public function add(){
		$this->View(App::newAddUserView());
		$this->View->Model($this->Model);
		$this->View->setForm($this->getUpdateForm(true));
		$this->View->render();
	}
	
	public function index(){
		echo '<h1>User Dashboard</h1>';
	}
	
?>

Here we have extended the Controller class to create a Users class. The Users class will be the controller that handles all requests to add/edit/disable users in our app. We have added a __construct() method, which sets the Model to a new instance of our UserStore (using our dependency injection container, the code added to the container is shown below). We then define a method getUpdateForm() that generates an update form using our UserStore Model.

We then create an add() method, which we will call by setting our URL to ourapp/Users/add. Our App class will then route our request to the Users controller, and call the add() method. In this method, we create a new AddUserView, we then set the View’s Model to our UserStore, we then set the View’s Form to a new user form using the getUpdateForm() method, with $New = true, and we then call the View’s render() method to display the form to the user.

We now need to create the AddUserView.

<?PHP	// core/container.class.php -- additions only --
	
	public static function newUsers(){
		return new Users();
	}

	public static function newAddUserView(){
		return new AddUserView();
	}

	public static function newUserStore(){
		return new UserStore(self::newGateway('Users'), self::newAppCache());
	}
?>
	
<?PHP	// views/adduserview.class.php

class AddUserView extends View{
	
	private $Form;
	private $Content;
	
	public function __construct(){
		$this->setContent();
	}
	
	public function setForm($Form){
		$this->Form = $Form;
	}
	
	public function render(){
		echo $this->Content;
		if($this->Form->validate()){
			$this->Form = $this->Model->processUpdateForm();
		}
		$this->Form->render('*horizontal');		
	}
	
	public function setContent(){
		$Content = '<h1>Add a New User</h1>';
		$Content .= '<p>Use this form to add a new user.';
		$this->Content = $Content;
	}
	
	
}
	
?>

In our AddUserView, we have set a __construct() method that sets some static content to be displayed, using the setContent() method defined later in the class. We then have a setForm() method that is used by our Controller, to set the correct form to add a user. We then have a render() method that our controller calls to render the view to the browser. This method does a few things.

First, it displays the static content stored in the $this->Content variable. Next, it attempts to validate the form that has been set. If the form validates, we then ask the Model (our UserStore) to process the update form using processUpdateForm(). Whether the form validates or not, we then render the form so it is displayed to the user.

If the form was processed, it is regenerated as an update form, and prefilled with data, and a message to tell us the user was updated. If it was not processed, it will display any data the user has already entered, or if it is the first time it has been loaded it will display an empty form.

We have now set up the Model, the View, and the Controller. So we can now view this form by running the Google App Engine launcher, and launching our App, and visiting localhost:[port number displayed in launcher]/Users/add

It should look like the screenshot below.

Don’t worry too much about the appearance of our page at this stage, we will customise the look and feel of our App in a later article. You can experiment with this form, leave fields blank, or enter different passwords in the password fields and you will get an error message explaining what is missing or incorrect. If you do add valid data, you will see the User gets added and you get the update user form displayed on screen.

We can follow the same MVC process to add an edit view, and a disable view, and handle these using our controller and model. The complete Users controller, and UserStore are shown below, as well as the EditUserView and DsiableUserView.

<?PHP	// controllers/users.class.php

class Users extends Controller{
	
	public function __construct(){
		$this->Model(App::newUserStore());
	}
	
	private function getUpdateForm($New = false){
		$UpdateForm = $this->Model->getUpdateForm($New);
		return $UpdateForm;
	}
	
	public function add(){
		$this->View(App::newAddUserView());
		$this->View->Model($this->Model);
		$this->View->setForm($this->getUpdateForm(true));
		$this->View->render();
	}
	
	public function edit(){
		$this->View(App::newEditUserView());
		$this->View->Model($this->Model);
		$this->View->render();
	}
	
	public function disable(){
		$this->View(App::newDisableUserView());
		$this->View->Model($this->Model);
		$this->View->render();
	}
	
	public function index(){
		echo '<h1>User Dashboard</h1>';
	}
	
}
	
?>
	
<?PHP	// models/userstore.class.php
	
class UserStore extends CacheStore{
	
	private $User;
	
	public function buildSchema(){
		$Schema = new GDSSchema('User');
		$Schema->addString('Username', true);
		$Schema->addString('Password', false);
		$Schema->addString('Name', true);
		$Schema->addString('Surname',true);
		$Schema->addString('Email', true);
		$Schema->addString('CurrentCompany', true);
		$Schema->addBoolean('Active', true);
		return $Schema;
	}
	
	public function __construct($Gateway, $Cache){
		parent::__construct($this->buildSchema(), $Gateway, $Cache);
		$this->setEntityClass('User');
	}
	
	public function getUpdateForm($New = false){
		$Form = new AppForm('EditUser');
		$Form->add('label', 'labelUsername', 'Username', 'Username: ');
		if($New){
			$Username = $Form->add('text', 'Username');
			$Username->set_rule(array(
				'required' => array('error','Username is required!'),
			));
		} else{
			if(isset($_POST['UserKey'])){
				$this->User = $this->fetchById($_POST['UserKey']);
				if(!$this->User){
					throw new UserException('Trying to update a user, but the UserKey provided returned no valid user');					
				} else{
					$Username = $Form->add('text','Username', $this->User->Username, array('disabled' => 'disabled'));
				}

			} else{
				throw new UserException('Trying to update a user, but no UserKey has been supplied. $_POST[UserKey] not set.');
			}
		}
		
		if($New){
			$Form->add('label', 'labelPassword','Password','Password: ');
			$Password = $Form->add('password','Password');
			$Password->set_rule(array(
				'required' => array('error','Password is required!'),
				'length' => array(8,0,'error','Password must be at least 8 characters long.'),
			));
			
			$Form->add('label','labelConfirmPassword','ConfirmPassword','Confirm Password: ');
			$ConfirmPassword = $Form->add('password','ConfirmPassword');
			$ConfirmPassword->set_rule(array(
				'compare' => array('Password','error','Passwords do not match!'),
			));
		}
		
		$UName = '';
		$USurname = '';
		$UEmail = '';
		
		if(isset($this->User)){
			$UName = $this->User->Name;
			$USurname = $this->User->Surname;
			$UEmail = $this->User->Email;
		}
		
		$Form->add('label','labelName','Name','Name: ');
		$Name = $Form->add('text','Name', $UName);
		$Name->set_rule(array(
			'required' => array('error','Your name is required.'),
		));
		
		$Form->add('label','labelSurname','Surname','Surname: ');
		$Surname = $Form->add('text','Surname', $USurname);
		$Surname->set_rule(array(
			'required' => array('error','Your surname is required.'),
		));
		
		$Form->add('label','labelEmail','Email','Email: ');
		$Email = $Form->add('text','Email', $UEmail);
		$Email->set_rule(array(
			'required' => array('error','Your email is required.'),
			'email' => array('error','Your email address is not valid'),
		));
		
		if(isset($this->User)){
			$Form->add('hidden','UserKey', $this->User->getKeyId());
		}
		
		$Form->add('submit','btnSubmit','Save User');
		
		$this->UpdateForm = $Form;
		return $Form;
	}
	
	public function processUpdateForm(){
		if(!isset($_POST['UserKey'])){
			$this->User = new User();
			$this->User->Username = $_POST['Username'];
			$this->User->Password = $this->generatePassword($_POST['Password']);
		} else{
			$this->User = $this->fetchById($_POST['UserKey']);
			if(!$this->User){
				throw new UserException('Trying to process user update form, but the UserKey provided returned no valid user');
			}
		}
		
		$this->User->Name = $_POST['Name'];
		$this->User->Surname = $_POST['Surname'];
		$this->User->Email = $_POST['Email'];
		
		$this->setCurrentCompany('ExampleCompany');
		$this->User->Active = true;
		
		$this->upsert($this->User);
		
		$this->User->setKeyId($this->findNewKeyId($this->User->Username));
		
		$_POST['UserKey'] = $this->User->getKeyId();
		
		$this->getUpdateForm(false);
		
		$this->UpdateForm->add_error('error', 'The user '.$this->User->Username.' has been updated.');
		return $this->UpdateForm;
			
	}
	
	private function generatePassword($Password){
		$Hash = password_hash($Password, PASSWORD_BCRYPT);
		return $Hash;
	}
	
	private function findNewKeyId($Username){
		$User = $this->fetchOne("Select * FROM User WHERE Username = @Username", ['Username' => $Username]);
		$UserKey = $User->getKeyId();
		return $UserKey;
	}
	
	public function getPasswordUpdateForm(){
		if(!isset($_POST['UserKey'])){
			throw new UserException('Trying to generate password update form, but $_POST[UserKey] is not set');
		}
		
		$Form = new AppForm('UpdatePassword');
		
		$Form->add('label','labelPassword','Password','New Password: ');
		$Password = $Form->add('password','Password');
		$Password->set_rule(array(
			'required' => array('error','Password is required!'),
			'length' => array(8,0,'error','Password must be at least 8 characters'),
		));
		$Form->add('label','labelConfirmPassword','ConfirmPassword','Confirm Password: ');
		$ConfirmPassword = $Form->add('password','ConfirmPassword');
		$ConfirmPassword->set_rule(array(
				'compare' => array('Password','error','Passwords do not match!'),
		));
		
		$Form->add('hidden','UserKey',$_POST['UserKey']);
		
		$Form->add('submit','btnSubmit','Update Password');
		
		return $Form;
	}
	
	public function processUpdatePassword(){
		if(!isset($_POST['UserKey'])){
			throw new UserException('Trying to process password update, but $_POST[UserKey] not set');
		}
		$User = $this->fetchById($_POST['UserKey']);
		if(!$User){
			throw new UserException('Trying to process password update, but $_POST[UserKey] does not return a valid user');
		}
		$User->Password = $this->generatePassword($_POST['Password']);
		
		$this->upsert($User);
	}
	
	public function setCurrentCompany($Company){
		$this->User->CurrentCompany = $Company;
	}
	
	public function getUserListByCompany($Company, $Active = true){
		$List = $this->fetchAll("SELECT * FROM User WHERE CurrentCompany = @Company AND Active = @Active", ['Company' => $Company, 'Active' => $Active]);
		if(sizeof($List) == 0){
			return false;
		} else{
			return $List;
		}
	}
	
	public function getUserSelectFormByCompany($Company, $Active = true){
		$UserList = $this->getUserListByCompany($Company, $Active);
		if(!$UserList){
			$Form = new AppForm('UserSelect');
			$Form->add('label','labelUserKey','UserKey','User: ');
			$Form->add('text','UserKey','No Users', array('disabled' => 'disabled'));
		} else{
			
			$Options = [];
			foreach($UserList AS $User){
				$Options[$User->getKeyId()] = $User->Username.' - '.$User->Name.' '.$User->Surname;
			}
			
			$Form = new AppForm('UserSelect');
			$Form->add('label','labelUserKey','UserKey','User: ');
			$UserKey = $Form->add('select','UserKey');
			$UserKey->add_options($Options);
			
			$Form->add('submit','btnSubmit','Select User');
		}
		
		return $Form;
	}
	
	public function getUserDisableForm(){
		if(!isset($_POST['UserKey'])){
			throw new UserException('Trying to generate disable user, but $_POST[UserKey] has not been set');
		}
		$User = $this->fetchById($_POST['UserKey']);
		$Form = new AppForm('DeleteUser');
		$Form->add('hidden','UserKey',$_POST['UserKey']);
		$Form->add('label','labelSubmit','btnSubmit','Do you want to disable access for '.$User->Name.' '.$User->Surname.' - '.$User->Username);
		$Form->add('submit', 'btnSubmit','Disable User');
		
		return $Form;
	}
	
	
	public function processUserDisableForm(){
		if(!isset($_POST['UserKey'])){
			throw new UserException('Trying to disable a user, but $_POST[UserKey] has not been set');
		}
		$User = $this->fetchById($_POST['UserKey']);
		$User->Active = false;
		$this->upsert($User);
	}
		
}	
?>
	
<?PHP	// views/edituserview.class.php

class EditUserView extends View{
	
	private $Content;
	private $SelectForm;
	private $EditForm;
	private $UpdatePasswordForm;
	
	public function __construct(){
		$this->setContent();
	}
	
	public function render(){
		if(!isset($_POST['UserKey'])){
			echo $this->Content;
			$this->setSelectForm();
			$this->SelectForm->render('*horizontal');
		} else{
			
			$this->setEditForm();
			$this->setUpdatePasswordForm();
			
			if($this->EditForm->validate()){
				$this->Model->processUpdateForm();
				$this->setEditForm();
			}
			
			if($this->UpdatePasswordForm->validate()){
				$this->Model->processUpdatePassword();
			}
			
			echo $this->Content;
			
			$this->EditForm->render('*horizontal');
			$this->UpdatePasswordForm->render('*horizontal');
		}
	}
	
	public function setContent(){
		$Content = '<h1>Edit a User</h1>';
		$Content .= '<p>Select a user to edit';
		$this->Content = $Content;
	}
	
	public function setSelectForm(){
		$this->SelectForm = $this->Model->getUserSelectFormByCompany('ExampleCompany');
	}
	
	public function setEditForm(){
		$this->EditForm = $this->Model->getUpdateForm(false);
	}
	
	public function setUpdatePasswordForm(){
		$this->UpdatePasswordForm = $this->Model->getPasswordUpdateForm();
	}
	
}
	
?>
	
<?PHP	// views/disableuserview.class.php

class DisableUserView extends View{
	
	private $Content;
	private $SelectForm;
	private $DisableForm;
	
	public function __construct(){
		$this->setContent();
	}
	
	public function render(){
		if(!isset($_POST['UserKey'])){
			echo $this->Content;
			$this->setSelectForm();
			$this->SelectForm->render('*horizontal');
		} else{
			
			$this->setDisableForm();
			
			if($this->DisableForm->validate()){
				$this->Model->processUserDisableForm();
				echo '<p>User Disabled';
			} else{
				$this->DisableForm->render('*horizontal');				
			}
		}
	}
	
	public function setContent(){
		$Content = '<h1>Disable a User</h1>';
		$Content .= '<p>Select a user to disable';
		$this->Content = $Content;
	}
	
	public function setSelectForm(){
		$this->SelectForm = $this->Model->getUserSelectFormByCompany('ExampleCompany');
	}
	
	public function setDisableForm(){
		$this->DisableForm = $this->Model->getUserDisableForm();
	}
	
}
	
?>

You may notice that for both the disable user view and edit user view there is a SelectForm added. This simply looks up all users in a particular company, and generates a select form. When the form is validated, the UserKey will be set, allowing the edit user or disable user form to be loaded correctly.

We are now able to add, edit and disable users in our App. It is important to note however, that when the Google App Engine Launcher restarts in the development environment, and data stored in the Datastore will be lost. When we begin working with authentication, we will write a setup script that allows us to add enough data to use the system. Data is not lost when we go live on App Engine! I have not included PHPUnit tests here, as the article is already very lengthy. From this article onwards, I will not include the test files, as it adds too much to each article. Rest assured that tests are happening behind the scenes! And will be available when the app is released in a downloadable format at the end of this series.

In the next article we will begin adding authentication to our App, and authenticating against the Users we are now able to manage.