Creating a guestbook with CakePHP part 2

In my first post I showed how to setup your development environment, create your tables and use the CakePHP command line utility to bake the models, controllers and views. I’m now going to expand upon this bare bones guestbook by adding in some authentication.

Adding authentication

If you’ve looked at the controllers we baked, you’ll see they extend AppController. AppController is a handy place to put code which will effect all controllers. If your project does not have its own app controller, CakePHP uses its default one. In our case the app controller is a good place to configure our authentication. We’ll start by creating the file ‘app\app_controller.php’. Note we do not place it in ‘app\controllers’. Here’s what we’ll put in that file:

<?php
class AppController extends Controller {
    var $components = array('Auth', 'Session');

    function beforeFilter() {
        $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
        $this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
        $this->Auth->loginRedirect = array('controller' => 'posts', 'action' => 'adminindex');
    }
}
?>

Here we’ve declared we will be using the Auth component. This is a useful CakePHP component which will handle the authentication functionality for us.  The beforeFilter() function is called before each controller action, here we setup where our login action will be and where it should redirect to once the user has logged in.

We now need to create the login action on the users controller. Open up ‘app\controllers\users_controller.php’. Inside the UsersController class, add the following functions:

	function login() {
		if ($this->Session->read('Auth.User')) {
			$this->Session->setFlash('You are now logged in');
			$this->redirect('/', null, false);
		}
    }

	function logout() {
		$this->Session->setFlash('You are now logged out');
		$this->redirect($this->Auth->logout());
	}

We now have login and logout actions. Each action in our controller requires a view. When we created our app controller we set the logout view to be the login screen, in the code above we redirect to this when the user logs out. This means we only need to add a view for the login screen.

Create the file ‘app\views\users\login.ctp’ and enter the code below:

<?php
	$session->flash('auth');
	echo $form->create('User', array('action' => 'login'));
	echo $form->inputs(array(
		'username',
		'password'
		));
	echo $form->end('Login');
?>

The first thing we do here is to display any messages we set in the controller. When we refer to $session we are actually referring to an instance of SessionHelper created for us by CakePHP. We then use FormHelper to start the login form. We tell it the model this form is for and the action the form should submit to. We then use the FormHelper inputs function to create our input boxes for username and password. We could go further and define custom labels, but we’ll just stick with the defaults.

You should be able to visit http://localhost/guestbook/users/login. Note that if you now try and visit any page, you will be redirected to login. By default CakePHP authentication locks down all controller actions. However we need to create a user to be able to login. To get around this, add this temporary function to users_controller.php:

	function beforeFilter() {
		$this->Auth->allow('add');
    }

This tells CakePHP to allow anyone to access this action, even if they aren’t logged in. Go ahead and visit http://localhost/guestbook/users/add. Add a user, making sure to keep a note of the details you enter. Now remove the code we just added and go to http://localhost/guestbook/users/login. You should be able to login successfully.

We now have our guestbook protected by authentication, however we have everything protected. We need to loosen the restrictions. The two things we want anyone to be able to do are view the guestbook and add a new post. To do this we simply add the following into the PostsController class found at ‘app\controllers\posts':

	function beforeFilter() {
		$this->Auth->allow('index', 'add');
    }

One final change we need to make is to change the main page. At the moment we’re asked to login when we visit http://localhost/guestbook. We want this URL to go to the list of guestbook posts. There are a number of ways to do this but arguably the simplist is to change routes.php. You can find this file in ‘app\config’.

Replace the line:

Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));

With:

Router::connect('/', array('controller' => 'posts', 'action' => 'index', 'home'));

That’s all the functionality complete. Logged in users can do anything and guests can view and add a post to our guestbook. You’ve probably noticed however that it doesn’t look much like a guestbook. Normally a guestbook would have separate admin and public interfaces, here ours looks like one big admin console. Luckily CakePHP has an elegant solution in the form of layouts. At the moment CakePHP is using the default layout file found at ‘cake\libs\view\layouts\default.ctp’. Copy this file to ‘app\views\layouts’. The default layout is quite adequate for the admin area, so we’ll just make some simple modifications.

Changing the layout

First of all, you may have noticed the SQL dump which appears at the bottom of each page, lets tidy things up by removing it.

Open ‘app\views\layouts\default.ctp’, find and remove the line:

<?php echo $this->element('sql_dump'); ?>

There’s also some text which reads “CakePHP: the rapid development php framework” in a couple of places. I’m going to change these to “Guestbook admin console”, except the one in the footer which refers to the CakePHP logo.

That’s our admin view sorted, onto the public view. Create the file public.ctp in the ‘app\views\layouts’ folder. I’m going to base the look of the guestbook on Perhypex:Guest. Here’s what you should put in the public.ctp file:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<?php echo $this->Html->charset(); ?>
	<title>
		Guestbook:
		<?php echo $title_for_layout; ?>
	</title>
	<?php
		echo $this->Html->meta('icon');
		echo $this->Html->css('public');
		echo $scripts_for_layout;
	?>
</head>
<body>
	<div id="container">
		<div id="header">
			My Guestbook
		</div>
		<div id="page_container">
			<?php echo $this->Session->flash(); ?>
			<?php echo $content_for_layout; ?>
		</div>
		<div id="footer">
			Thanks for visiting!
		</div>
	</div>
</body>
</html>

This is a simple layout in which we tell CakePHP to use the public css file. We’ll create that file now. In ‘app\webroot\css’ create the file public.css with the following content:

body {
background-color:#ffffff;
margin:0px;
padding:0px;
font-family:Arial, Helvetica, sans-serif;
color:#000000;
}

#header {
background-color:#990000;
color:#ffffff;
text-align:center;
height:80px;
font-size:30pt;
padding-top:10px;
}

/* Contains all the comments */
#page_container {
width:750px;
margin-right:auto;
margin-left:auto;
}

/* Makes elements float to the left */
.align_left {
float:left;
}

/* Makes elements float to the right */
.align_right {
float:right;
}

/* Contains the view or sign guestbook and return to site options */
.options {
padding:30px 0 30px 0;
}

/* The style for options links */
.options a{
font-size:16pt;
}

/* Box which contains the comment */
.comment {
border:2px solid #990000;
margin:10px 0 10px 0;
}

/* Appears at the top of the comment box with the name and date */
.comment_header {
background-color:#990000;
padding:5px;
font-size:16pt;
color:#ffffff;
overflow:auto;
}

/* contains extra fields such as the location */
.extra_fields {
background-color:#c3c5c3;
color:#000000;
padding:10px;
}

/* Contains the persons actual comment */
.comment_body {
padding:10px;
}

/* Section which contains the page numbers */
.page_number {
text-align:center;
padding:10px;
}

/* Appears at the bottom with a link back to perhypex:guest */
#footer {
text-align:center;
padding:20px;
color:#b82929;
}

/* Global style for the links on the page */
a {
color:#990000;
font-weight:bold;
text-decoration:underline;
}

/* How visited links should change */
a:visited {
font-weight:normal;
}

/* How a link should change when the cursor is over it */
a:hover {
text-decoration:none;
}

/* Style for the form the user fills in */
#comment_form {
margin-top:20px;
}

/* Style for the input labels */
label {
font-weight:bold;
}

/* Style for the area in which the user enters their comment */
textarea {
font-family:Arial, Helvetica, sans-serif;
}

Now we just need to tell the index and add functions to use this layout. Add the following as the first line of the index and add functions inside ‘app\controllers\posts_controller.php':

$this->layout = 'public';

We now just need to customise the add and index action views.

Replace the contents of ‘app\views\posts\add.ctp’ with the following:

<div class="options">
	<span class="align_left">
		<?php echo $this->Html->link(__('View the guestbook', true), array('action' => 'index')); ?>
	</span>
</div>

<div class="posts form">
	<?php echo $this->Form->create('Post');?>
		<fieldset>
			<legend><?php __('Sign the guestbook'); ?></legend>
		<?php
			echo $this->Form->input('name');
			echo $this->Form->input('body', array('type' => 'textarea'));
		?>
		</fieldset>
	<?php echo $this->Form->end(__('Submit', true));?>
</div>

We now need to change the ‘app\views\posts\index.ctp’ file. The current layout of this file will be useful as an admin index, so rename it to adminindex.ctp. Next create the file index.ctp and add the following content:

<div class="posts index">

	<div class="options">
		<span class="align_left">
			<?php echo $this->Html->link(__('Sign the guestbook', true), array('action' => 'add')); ?>
		</span>
		<span class="align_right">
			<?php echo $this->Paginator->sort('Sort by date posted', 'created'); ?>
		</span>
	</div>

	<?php
	foreach ($posts as $post):?>
		<div class="comment">
			<div class="comment_header">
				<span class="align_left"><?php echo $post['Post']['name']; ?></span>
				<span class="align_right"><?php echo date("F dS, Y", strtotime($post['Post']['created'])); ?></span>
			</div>
			<div class="comment_body">
				<?php echo $post['Post']['body']; ?>
			</div>
			<?php
			if($post['Post']['modified'] != $post['Post']['created']) { ?>
				<div class="extra_fields">
					Edited by the guestbook owner on
					<?php
					echo date("F dS, Y", strtotime($post['Post']['modified']));
					?>
				</div>
			<?php
			}
			?>
		</div>
	<?php endforeach; ?>

	<p>
	<?php
	echo $this->Paginator->counter(array(
	'format' => __('Page %page% of %pages%', true)
	));
	?>	</p>

	<div class="paging">
		<?php echo $this->Paginator->prev('<< ' . __('previous', true), array(), null, array('class'=>'disabled'));?>
	 | 	<?php echo $this->Paginator->numbers();?>
 |
		<?php echo $this->Paginator->next(__('next', true) . ' >>', array(), null, array('class' => 'disabled'));?>
	</div>
</div>

Add a few posts and see how it now looks. You may notice one minor problem: the posts are listed from oldest to newest, not exactly how a guestbook works. CakePHP offers a simple way to customise this though. Open up ‘app\controllers\posts_controller.php’. Below

var $name = 'Posts';

add the following:

var $paginate = array(
		'limit' => 10,
		'order' => array(
			'Post.created' => 'desc')
		);

Here we’ve said that we want to view 10 posts per page and by the created date descending.

We’re very nearly done, the only thing we are missing is the action to view the admin index page. Unlike the public page, this page will contain the links to edit and delete posts.

Inside ‘app\controllers\posts_controller.php’, add the following function:

	function adminIndex() {
		$this->Post->recursive = 0;
		$this->set('posts', $this->paginate());
	}

That’s it, our guestbook application is complete!

Viewing the guestbook

Viewing the guestbook

Signing the guestbook

Signing the guestbook

This entry was posted in CakePHP, PHP and tagged , . Bookmark the permalink.

3 Responses to Creating a guestbook with CakePHP part 2

  1. Camci says:

    Hey, thank you for this good and easy guide! I have one problem tho it seems like I cant get the layout working like you, I did follow the guide, however Im confused at one part.
    Where it says add the following line first at index and add functions, im a bit confused, where am I supose to add ” $this->layout = ‘public'; ” ?

    Best Regards

    • ashley says:

      Hi, thanks for the comment. Sorry if this wasn’t clear, ‘$this->layout = ‘public’;’. Should appear as the first line of the index and add functions inside your post controller. Thus, they will look as follows:

      Index:

      function index() {
      $this->layout = 'public';

      Add:

      function add() {
      $this->layout = 'public';

      Retaining the code below each of course. This tells the function to use the public layout which is the layout visitors will see.

      Please let me know if your have any further issues.

  2. pankaj says:

    Very Nice Article i appreciate it too.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>