Blog Tutorial with CakePHP Framework

In a follow up to my previous article: blog tutorial with Fat-Free Framework I have tried to make exactly the same application but using the CakePHP Framework – to contrast using a micro-framework with a fully featured one.
CakePHP works with PHP 4 or 5 so this should work on most servers.

Step 1: Setup

Download CakePHP from the website. This tutorial uses version 1.3.11.
It runs happily from its own folder in a web site (so you don’t need to set up a separate virtual site) – I just used a folder called cake1, with the contents looking like this
CakePHP root directory

Step 2: Bootstrapping

We’ll use this section to configure the database connection.
Copy /app/config/database.php.default to database.php in the same folder.
Edit the values in the $default array to match your database.
You should also edit /app/config/core.php and change the values for Security.salt and Security.cipherSeed.
Make the app/tmp folder writable for the webserver by chown -R www-data app/tmp.
As we’re developing it may be worth disabling caching so in /app/config/core.php so uncomment

Configure::write('Cache.disable', true);

Step 3: Routing

With CakePHP a lot of routing happens automagically by putting correctly names files in the right places.
However we need to tell it a route for the homepage of the site.
Edit /app/config/routes.php and comment out all the default route(s), replacing with one to the articles controller.

//Router::connect('/', array('controller' => 'pages', 'action' => 'index'));
Router::connect('/', array('controller' => 'articles', 'action' => 'index'));

Step 4: Models

CakePHP needs the database to follow a naming convention. The table names should be plural so we’ll use articles and users.
CakePHP will dynamically creates model objects for you if it cannot find corresponding files in /app/models, as this is a tutorial about getting a prototype running quickly we’ll take advantage of this here.
Here’s the SQL that will set you up with the 2 tables necessary for this tutorial, it is exactly the same as for my Fat-Free Framework tutorial:

CREATE DATABASE `blog` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
 
USE `blog`;
 
CREATE TABLE IF NOT EXISTS `articles` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `timestamp` datetime NOT NULL,
  `title` VARCHAR(128) NOT NULL,
  `summary` VARCHAR(128) NOT NULL,
  `content` text NOT NULL,
  `author` VARCHAR(128) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 
INSERT INTO `articles` (`id`, `timestamp`, `title`, `summary`, `content`, `author`) VALUES
(1, '2011-07-28 02:03:14', 'Hello World!', 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut ', 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', 'Mr White'),
(2, '2011-07-28 02:03:14', 'More Hello World!', 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut ', 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', 'Mr Green');
 
CREATE TABLE IF NOT EXISTS `users` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(255) NOT NULL,
  `password` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
INSERT INTO `users` (`id`, `name`, `password`) VALUES
  ('1', 'admin', 'password');

Step 5: Application Front End

First create a layout that will be used as a template for the individual views /app/views/layouts/default.ctp:

<!DOCTYPE html>
<html>
  <head>
  <title><?php echo $title_for_layout?></title>
</head>
<body>
  <?php echo $content_for_layout ?>
</body>
</html>

Doing this will override the default CakePHP layout which means you will loose the nice diagnostic section that tells you what queries are being sent to the database. You can get this back whenever you want by temporarily renaming the layout default.ctp to something like default.html

Now the layout is in place, create a file called articles_controller.php inside /app/controllers with the code below. The index function provides the logic for the homepage and tells it to retrieve all articles and the set command passes the data to the view.

<?php
class ArticlesController extends AppController {
	var $helpers = array ('Html','Form');
	var $name = 'Articles';
	function index() {
		$this->set('articles', $this->Article->find('all'));
	}
}
?>

Add in another function to deal with the detail view which gets all the data for an individual record.

function view($id = null) {
	$this->Article->id = $id;
	$this->set('article', $this->Article->read());
}

Now the logic is in place lets deal with the views. Create a file for the home page /app/views/articles/index.ctp.

<p>Blog Titles</p>
<?php foreach ($articles as $article): ?>
	<p><?php echo $this->Html->link($article['Article']['title'], array('controller' => 'articles', 'action' => 'view', $article['Article']['id'])); ?> by <?php echo $article['Article']['author']; ?></p>
	<p><?php echo $article['Article']['summary']; ?></p>
<?php endforeach; ?>

This is more complicated than the Fat-Free Framework version and begins to show where using a micro framework may save you a lot of typing.
The file for the details view should be saved as /app/views/articles/view.ctp.

<?php echo $article['Article']['title']?>
<p>Published: <?php echo $article['Article']['timestamp']?> by <?php echo $article['Article']['author']?></p>
<?php echo $article['Article']['content']?>
<p><a href='../'>Back to Homepage</a></p>

You can now check this all works by visiting /articles/index with a browser.

Step 6: Application Back End

To mimic the previous tutorial I did, the whole admin section needs to accessed under a /admin URL.
This is built into CakePHP and you can achieve it by editing /app/config/core.php and uncommenting

Configure::write('Routing.prefixes', array('admin'));

You can now add functions like admin_index() to a controller and these are only available when using an admin prefix in the URL like /admin/articles.
The admin home page is very similar to the normal home page with some extra links to add/edit/delete blog articles, so the controller method looks like this:

function admin_index() {
	$this->set('articles', $this->Article->find('all'));
}

And the view, saved as /app/views/articles/admin_index.ctp looks like this:

<h1>My Blog Administration</h1>
<p><?php echo $this->Html->link('Add Article', array('controller' => 'articles', 'action' => 'admin_add')); ?></p>
<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Date</th>
      <th>Author</th>
      <th colspan='2'>Actions</th>
    </tr>
  </thead>
  <tbody>
  <?php foreach ($articles as $article): ?>
    <tr>
      <td><?php echo $article['Article']['title']; ?></td>
      <td><?php echo $article['Article']['timestamp']; ?></td>
      <td><?php echo $article['Article']['author']; ?></td>
      <td><?php echo $this->Html->link('Edit', array('controller' => 'articles', 'action' => 'admin_edit', $article['Article']['id'])); ?></td>
      <td><?php echo $this->Html->link('Delete', array('controller' => 'articles', 'action' => 'admin_delete', $article['Article']['id'])); ?></td>
    </tr>
  <?php endforeach; ?>
  </tbody>
</table>

You can now access this via /admin/articles/, we’ll add the authentication in the next step.

CakePHP uses something called the Flash to communicate temporary messages to the user (don’t confuse with Adobe Flash).
The following code in a view will display the message if present:

<?php echo $this->Session->flash(); ?>

We will add this to the admin_index view under the H1 tag so that messages from the add/edit/delete actions can be displayed.
Also the following code is required to make the component available in the controller:

var $components = array('Session');

Note that built into CakePHP is a mechanism for confirming the delete of these records:

<?php echo $this->Html->link('Delete', array('action' => 'admin_delete', $article['Article']['id']), null, sprintf('Are you sure you want to delete # %s?', $article['Article']['id'])); ?>

This is starting to get complicated by the number of parameters you have to pass this method. I you use this every day then maybe it will become second nature, however for a beginner it can be a little daunting.

Add/Edit/Delete
To keep things simple we will create separate actions and views for the add/edit functionality – even though there is a lot of similarity between the two.

function admin_add() {
	if (!empty($this->data)) {
		if ($this->Article->save($this->data)) {
			$this->Session->setFlash('Your article has been saved.');
			$this->redirect(array('action' => 'index'));
		}
	}	
}
 
function admin_edit($id = null) {
	$this->Article->id = $id;
	if (empty($this->data)) {
		$this->data = $this->Article->read();
	} else {
		if ($this->Article->save($this->data)) {
			$this->Session->setFlash('Your article has been saved.');
			$this->redirect(array('action' => 'index'));
		}
	}
}
 
function admin_delete($id) {
	if ($this->Article->delete($id)) {
		$this->Session->setFlash('The article has been deleted.');
		$this->redirect(array('action' => 'index'));
	}
}

Delete doesn’t require a view as the action is confirmed by use of the flash. Edit is nearly the same as Add except for a few extra lines to retrieve the record first.
CakePHP uses a method called ‘save’ to save changes to the database, but behind the scenes this is going to be an insert or update. If Cake gets passed an id field via the submitted form it assumes you are going to be updating a record.

Thoughts at this stage – can clearly see the difference between a micro framework and a full size one now, Cake is more complex, there’s more to learn (much more documentation) and it takes longer.
Now the views of Add and Edit, these are nearly identical except that Edit includes the id field in a hidden variable.
views/articles/admin_add.ctp

<h1>Add</h1>
<?php echo $this->Form->create('Article');?>
	<?php
		echo $this->Form->input('title');
		echo $this->Form->input('summary');
		echo $this->Form->input('content');
		echo $this->Form->input('author');
	?>
<?php echo $this->Form->end('Submit');?>

views/articles/admin_edit.ctp

<h1>Edit</h1>
<?php echo $this->Form->create('Article');?>
	<?php
		echo $this->Form->input('title');
		echo $this->Form->input('summary');
		echo $this->Form->input('content');
		echo $this->Form->input('author');
		echo $this->Form->input('id', array('type' => 'hidden'));
	?>
<?php echo $this->Form->end('Submit');?>

You can now test this in a browser and should be able to add/edit and delete records.

Step 7: Using Middleware

To implement authentication from a database table we use CakePHPs Authentication Component.
This gets messy if you want to do it differently to the CakePHP way – that means we need to create a new database table with different column names and use that instead.

DROP TABLE users;
CREATE TABLE users (
    id INTEGER AUTO_INCREMENT,
    username CHAR(50),
    password CHAR(40),
    PRIMARY KEY (id)
);

This requires a users controller called users_controller.php

<?php
class UsersController extends AppController {
 
    var $name = 'Users';    
    var $components = array('Auth');
 
    function login() {
    }
 
    function logout() {
        $this->redirect($this->Auth->logout());
    }
}

And the following 2 changes to articles_controller.php, update the components variable to include the Auth component and add a new function.

var $components = array('Session','Auth');  //changed to add Auth
 
function beforeFilter() {
	$this->Auth->allow('index', 'view');
	$this->Auth->loginAction = array('admin' => false, 'controller' => 'users', 'action' => 'login');
}

The beforeFilter function changes some of the defaults for the Auth component, the first line allows the index and view actions to be executed without requiring authentication. Without the second line CakePHP will look for an action called admin_login so this tells it to simply use the login action.
Now we need a view to contain a login form.
views/users/login.ctp

<?php
    echo $this->Session->flash('auth');
    echo $this->Form->create('User');
    echo $this->Form->input('username');
    echo $this->Form->input('password');
    echo $this->Form->end('Login');
?>

As the passwords are stored hashed in the database (which is a good idea) we have a minor annoyance here – we can’t insert a row for the admin user into the database manually as we don’t know what the password field needs to hold.
We can get around this by returning to the CakePHP default layout which contains useful debug information and see what SQL it is using.
Rename app/views/layouts/default.ctp to default.htm
Visit /admin/articles and input your chosen username and password (I used ‘admin’ and ‘password’ for this tutorial) then click submit, in the diagnostic queries section you will see what SQL it tried to use, copy the hashed version of the password and manually insert this into the users database table with your username.
CakePHP default template showing SQL
Now you can rename default.htm back to default.ctp and get your own layout back.

Thoughts – if you know what you are doing it can be set up with very little coding and it works well.
However it took me about 45 mins to figure it out from the documentation on their website.

Step 8: Summary

I guess it should have been obvious from the start but using a bigger framework will lead to more complexity and a steeper learning curve. In exchange for that you gain greater flexibility which aren’t being taken advantage of with such a simple system developed here. A some really important ones for me are the form helper, validation, built in pagination (with sorting) and custom DataSources. You may be interested in my article on creating a DataSource to read from a RESTful API which lets you page through and sort the data just as if it were from MySQL.

Rather than putting the whole framework into a zip file, I’ve just included the files that I created with this tutorial, they all exist within the app folder of the CakePHP installation.

controllers
	articles_controller.php
	users_controller.php
views
	articles
		admin_add.ctp
		admin_edit.ctp
		admin_index.ctp
		index.ctp
		view.ctp
	layouts
		default.ctp
	users
		login.ctp

Download CakePHP app folder.

I’m going to try this very same blog with CodeIgniter next.

2 Replies to “Blog Tutorial with CakePHP Framework”

  1. CakePHP framework represents a foundational structure for programmers to create web applications. Using it, the developers are enabled to work in a structured and rapid manner, without losing flexibility.

  2. CakePHP has a very different take compared to other frameworks. I prefer CodeIgniter and similar frameworks, but I think depending on application type, CakePHP is very productive framework.

Leave a Reply

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

CAPTCHA - answer the question below to prove you are human *