How to create a CMS with CakePHP

H

I really enjoying writing code and I find that snippets just don’t always cut it for me.  So in today’s article, I am going to describe the process of creating a CMS (Content Management System) with CakePHP.

This will be a two part article, in part one we will focus on getting the basics working.  The basics will include ability to add, edit, and delete static content pages.

Part two will advance on our basis and allow us to create drafts and revert back to previous versions.

To begin, let’s download the latest release of CakePHP.  After you’ve downloaded it, extract it to a folder of your choice.  In my case it will be c:\xampplite\htdocs\CMS.  I can now access my new web site by browsing to http://localhost/CMS.

At this point we now have our basic CakePHP web site installed.  To proceed further, we will need to update our database.php file in app/config.  Update the $default array with your database host, name, username, and password.  Once you are done, save the file.  Also inside this directory, we need to update our security salt.  I normally just randomly choose alphanumeric characters, about 32 characters in length.  Once we are done, we can save the file.

To begin working, we need to create our database and tables.  Create a new database with the same name specified in your database config file.  Ensure you have created the username and password as well.

Let’s continue by creating our users table.  Below is a sample CREATE script that will get you started.  If you require additional fields, you may add them in:

[code]CREATE TABLE  `users` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `first` varchar(45) NOT NULL,
  `last` varchar(45) NOT NULL,
  `email` varchar(45) NOT NULL,
  `username` varchar(45) NOT NULL,
  `password` varchar(45) NOT NULL,
  `created` datetime default NULL,
  `modified` datetime default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
[/code]

Knowing that our plans for part two will be to create drafts and revisions, let’s also create a pages table that will store our information.  At this point it won’t save drafts, but it will save us the time of back-populating the main page table.  If I wasn’t planning on allowing revision history, I would skip this step and perform directory options to retrieve my list of pages instead.  Here is a sample create statement for our pages table:

[code]CREATE TABLE  `pages` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `title` varchar(255) NOT NULL,
  `body` text NOT NULL,
  `user_id` int(10) unsigned NOT NULL,
  `created` datetime default NULL,
  `modified` datetime default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
[/code]

Do help us save a few lines of typing, let’s use CakePHP’s bakery to create our basic models, controllers, and views.  We will have to update some of these later, but it will save us some time.

First up is our model.  Perform the following commands:

[code]Click Start -> Run
Type: “cmd” without the quotes and click OK
Type: “cd \xampplite\htdocs\CMS” (update to your path) and press enter
Type: “cake\console\cake bake” and press enter
Type: “M” and press enter
Enter again (to use default database config)
Type: “1” and press enter to create the Page model
Type: “y” and press enter.  This will build our validation
Type: “29” and press enter
Type: “20” and press enter
Type: “20” and press enter
Type: “29” and press enter
Type: “29” and press enter
Type: “29” and press enter
Type: “y” and press enter to add a relationship
Type: “y” and press enter to add a relationship to user
Type: “n” and press enter
Type: “y” and press enter to confirm the relationship
Type: “n” and press enter to skip creating unit tests
Type: “M” and press enter
Enter again (to use default database config)
TYpe: “2” and press enter to create the User model
Type: “n” and press enter to skip validation
Type: “y” and press enter to add a relationship
Type: “y” and press enter to add a relationship to page
Type: “n” and press enter
Type: “y” and press enter to confirm the relationship
Type: “n” and press enter to skip creating unit tests
[/code]

We have now successfully created our two models.  Let’s continue by creating our controllers.  To create our controllers:

[code]Type: “C” and press enter
Type: “1” and press enter to create the pages controller
Type: “y” and press enter
Type: “n” and press enter
Type: “y” and press enter
Type: “n” and press enter
Type: “n” and press enter
Type: “n” and press enter
Type: “y” and press enter
Type: “y” and press enter
Type: “n” and press enter
Type: “C” and press enter
Type: “2” and press enter to create the users controller
Type: “n” and press enter
Type: “n” and press enter
Type: “y” and press enter
Type: “n” and press enter
[/code]

Now we have both our models and controllers created.  Let’s also create our views for our add and edit forms:

[code]Type: “V” and press enter
Type: “1” and press enter
Type: “y” and press enter
Type: “n” and press enter
[/code]

Let’s do a quick recap to summarize what we have done so far.

  1. Installed CakePHP
  2. Configured our setup to connect to our database
  3. Created our two database tables
  4. Used the bakery to create our models, controllers, and views

Coming up next.

  1. Update our pages controller when add, editing, and saving to write/delete files

Before we can begin, we need to fix a small issue that we have created by using the pages controller.  By default CakePHP will allow you create views in the pages folder and automatically display them.  I like this feature and have plans to use it in our CMS, so let’s copy the display() function from: cake/libs/controller/pages_controller.php to: app/controllers/pages_controller.php.  Be sure just to copy the individual function and not the entire file.

Next up, we need to fix our routes because by default CakePHP routes all pages/* to the above display function.  We do wish to use this function, but not for our standard list, add, edit, and delete.

Open the file app/config/routes.php and add the following lines (make sure you add these before the pages/* line):

[code] Router::connect(‘/pages/index’, array(‘controller’ => ‘pages’, ‘action’ => ‘index’));
 Router::connect(‘/pages/add’, array(‘controller’ => ‘pages’, ‘action’ => ‘add’));
 Router::connect(‘/pages/edit/*’, array(‘controller’ => ‘pages’, ‘action’ => ‘edit’));
 Router::connect(‘/pages/delete/*’, array(‘controller’ => ‘pages’, ‘action’ => ‘delete’));
[/code]

Let’s now update our pages_controller to save the file when we add and edit it and delete the file when we delete a page.  Below is our completed pages_controller.php

[code]class PagesController extends AppController {

 var $name = ‘Pages’;
 var $helpers = array(‘Html’, ‘Form’);

 function index() {
  $this->Page->recursive = 0;
  $this->set(‘pages’, $this->paginate());
 }

 function add() {
  if (!empty($this->data)) {
   $this->Page->create();
   if ($this->Page->save($this->data)) {
    // save a physical copy of the file
    $this->_saveFile($this->data);
    
    $this->Session->setFlash(__(‘The Page has been saved’, true));
    $this->redirect(array(‘action’=>’index’));
   } else {
    $this->Session->setFlash(__(‘The Page could not be saved. Please, try again.’, true));
   }
  }
  $users = $this->Page->User->find(‘list’);
  $this->set(compact(‘users’));
 }

 function edit($id = null) {
  if (!$id && empty($this->data)) {
   $this->Session->setFlash(__(‘Invalid Page’, true));
   $this->redirect(array(‘action’=>’index’));
  }
  if (!empty($this->data)) {
   if ($this->Page->save($this->data)) {
    // save a physical copy of the file
    $this->_saveFile($this->data);
    
    $this->Session->setFlash(__(‘The Page has been saved’, true));
    $this->redirect(array(‘action’=>’index’));
   } else {
    $this->Session->setFlash(__(‘The Page could not be saved. Please, try again.’, true));
   }
  }
  if (empty($this->data)) {
   $this->data = $this->Page->read(null, $id);
  }
  $users = $this->Page->User->find(‘list’);
  $this->set(compact(‘users’));
 }

 function delete($id = null) {
  if (!$id) {
   $this->Session->setFlash(__(‘Invalid id for Page’, true));
   $this->redirect(array(‘action’=>’index’));
  }
  
  // get our file name
  $this->Page->id = $id;
  $page = $this->Page->read();
  
  if ($this->Page->del($id)) {
   // delete our file
   $this->_deleteFile($page);
   
   $this->Session->setFlash(__(‘Page deleted’, true));
   $this->redirect(array(‘action’=>’index’));
  }
 }
 
 function _saveFile($data) {
  App::import(‘Sanitize’);
  // clean the file name and replace spaces with dashes
  // and save the file locally
  file_put_contents($this->_buildPath($data), $data[‘Page’][‘body’]);
 }
 
 function _deleteFile($data) {
  App::import(‘Sanitize’);
  // clean the file name and replace spaces with dashes
  // and delete the file locally
  if (file_exists($filepath = $this->_buildPath($data))) {
   unlink($filepath);
  }
 }
 
 function _buildPath($data) {
  return APP . ‘views\\pages\\’ . Sanitize::paranoid(str_replace(‘ ‘, ‘-‘, $data[‘Page’][‘title’]), array(‘-‘)) . ‘.ctp’;
 }
 
 /**
  * Displays a view
  *
  * @param mixed What page to display
  * @access public
  */
 function display() {
  $path = func_get_args();

  $count = count($path);
  if (!$count) {
   $this->redirect(‘/’);
  }
  $page = $subpage = $title = null;

  if (!empty($path[0])) {
   $page = $path[0];
  }
  if (!empty($path[1])) {
   $subpage = $path[1];
  }
  if (!empty($path[$count – 1])) {
   $title = Inflector::humanize($path[$count – 1]);
  }
  $this->set(compact(‘page’, ‘subpage’, ‘title’));
  $this->render(join(‘/’, $path));
 }

}
[/code]To add a new page go to: http://localhost/CMS/pages/add.  To view a list of your pages go to: http://localhost/CMS/pages/index.  To view a page that you have created go to: http://localhost/CMS/pages/page-title-with-nospaces-and-dashes-instead

That’s it for today’s tutorial.  For some extra-curricular activities, add login protection to the index(), add(), edit(), and delete() functions and update the add and edit functions and views to use the logged in user instead of the drop-down that the bakery created for us.

About the author

  • http://amjedonline.blogger.com Mohd Amjed

    Good Article,
    One small fix:
    Router::connect(‘/pages/index’, array(‘controller’ => ‘pages’, ‘action’ => ‘index’));

    should be

    Router::connect(‘/pages/index/*’, array(‘controller’ => ‘pages’, ‘action’ => ‘index’));

    else pagination wouldnt work, i spent half ofmy day, trying to figure out, why pagination was not working 🙂

  • cherif

    you must paste the display function from pages_controller in cake/libs/controller cause you intercepted it otherwise it will display errors.

  • http://tabvn.com Toan

    This is new CMS build on cakephp

    http://cakeui.com

  • Corey

    Hi, I am following along with this page and I have gotten to here:

    Click Start -> Run
    Type: “cmd” without the quotes and click OK
    Type: “cd \xampplite\htdocs\CMS” (update to your path) and press enter
    Type: “cake\console\cake bake” and press enter
    Type: “M” and press enter
    Enter again (to use default database config)
    Type: “1” and press enter to create the Page model
    Type: “y” and press enter. This will build our validation
    Type: “29” and press enter

    I a unable to see a 29 option. I am assuming that I have a later version of the CakePHP than you are writing about here. Can you explain what option 29 and 20 would do in the latest version? I am using 1.3.3.

    Thanks for your help.

  • Jamie

    Option 29 means do not validate this field. Option 20 is notEmpty.

  • Corey

    Ah, thank you sir.

  • Corey

    Ok, I followed all your instructions and went to add a page and I got the error below:

    Error: The view for PagesController::add() was not found.

    Error: Confirm you have created the file: C:\xampp\htdocs\CMS\app\views\pages\add.ctp

    Notice: If you want to customize this error message, create app\views\errors\missing_view.ctp

    (default) 3 queries took 4 ms
    Nr Query Error Affected Num. rows Took (ms)
    1 DESCRIBE `pages` 6 6 2
    2 DESCRIBE `users` 8 8 2
    3 SELECT `User`.`id` FROM `users` AS `User` WHERE 1 = 1

    Any clues? Bear in mind that I am a complete newbie at PHP in general and this is my very first foray into CakePHP.

  • Corey

    Nvm man, I figured it out. This article was a touch past my understanding. Once I did a couple of other tutorials and understood MVC better I was able to bake another view easily.

  • Anon

    Guys, I’d highly recommend avoiding this tutorial.

    It’s terrible

  • http://www.photogabble.co.uk Simon

    For some reason your SQL example above is showing up as random characters.

  • http://www.louboutinshoes.cc/jimmy-choo-c-83.html Jimmy Choo

    I agree with your blog, lucky to read your blog!

  • Samuel

    Hi,

    I’m on cake1.3, and the numbers your are given 20-29… and y-n… are not correct.

    Do you have it mabe for the latest version of cakephp (1.3) ?

    Thanks,
    Regards,

  • http://xyz javed

    hey dude i got stuck at
    Type: “cake\console\cake bake” and press enter
    \can you please help in my folder structure in console folder i donot have anythings as cake bake
    please help what is it ?????

  • http://www.gurutechnoworld.com/cms_application_services_chandigarh_india.aspx CMS Application

    Awesome post! It really helped me a lot! Thanks for sharing it.

  • http://www.gurutechnoworld.com/cms_application_services_chandigarh_india.aspx CMS Application

    Outstanding blogger, Thank you for presenting this important post. I found it useful. Best regards !!

  • Pingback: how to get rid of skin tags

  • Pingback: online PC repair

  • Pingback: Goozle Zone

  • Pingback: bad credit loans

  • Pingback: ania quisumbing

  • Pingback: one buck resume

  • Pingback: ultimate power profits

  • hellbreak

    Hi, I’ve just read you tutorial, but I’ve snowed under with this big issue.

    “Error: The view for PagesController::display() was not found.”

    my route.php is

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

    /**

    * …and connect the rest of ‘Pages’ controller’s urls.

    */

    Router::connect(‘/pages/index’, array(‘controller’ => ‘pages’, ‘action’ => ‘index’));

    Router::connect(‘/pages/add’, array(‘controller’ => ‘pages’, ‘action’ => ‘add’));

    Router::connect(‘/pages/edit/*’, array(‘controller’ => ‘pages’, ‘action’ => ‘edit’));

    Router::connect(‘/pages/delete/*’, array(‘controller’ => ‘pages’, ‘action’ => ‘delete’));

    Router::connect(‘/pages/*’, array(‘controller’ => ‘pages’, ‘action’ => ‘display’));

    and I use cakephp1.3

    can you help me to work it out please? I’d like to try your cms system

    Thank you

  • Rajat

    its greate

  • Pingback: Movie

  • Pingback: Recycle

  • Pingback: Elite

  • Pingback: Climate

  • Pingback: Crusade

By Jamie

My Books