CakePHP Tutorial: Storage, Baking and Slugs


In the previous article we built a movie database that enabled you to store all your movies in various formats, and showed the basics of getting a CakePHP project off the ground. This time around the block we’re going to use some of the same approaches to build a more complicated application that involves more models for data storage, and demonstrates the associations between models and just how darn easy it is.

We’re going to use some shortcuts and take advantage of the ‘bake’ functionality within CakePHP to cut down on the time it takes, so we can get to the pub a bit earlier.

Let’s get things moving by defining the database tables. We’ll be building a multiuser library that will enable many books to be stored and for those books to be associated with their owners. Also, we’ll show off a little and have some search engine-friendly URLs generated.

First up, create a new database to work with:

USE library;

Next, create the books and users tables, and feel free to add any extra details for each of the tables if you want to store more information than I am:

  user_id CHAR(36) NOT NULL,
  title VARCHAR(45) NOT NULL,
  slug VARCHAR(45) NOT NULL,
  author VARCHAR(45) NOT NULL,
  summary TEXT,
  purchased DATE,
  created DATETIME,
  modified DATETIME	   

  name VARCHAR(45) NOT NULL,
  created DATETIME,
  modified DATETIME	  

Run the above SQL statements on your MySQL database before moving on.

An introduction to baking

With your database tables ready and waiting, it’s time to perform some parlour tricks, CakePHP-style. Instead of manually creating the code for the controller, model and views, we’ll use CakePHP’s bake utility to generate the code for us.

I’ll assume you have downloaded the latest stable release of CakePHP 1.3 from GitHub ( or used Git to clone the repository.

For CakePHP’s bake utility to work, you need to have PHP available on the command line. This should be a matter of installing php-cli for your system.

Our users list, empty for now, but not for long... click the 'new user' link on the left and add a user for yourself.

Our users list, empty for now, but not for long... click the 'new user' link on the left and add a user for yourself.

Next, it’s handy to have the /cake/console path from the CakePHP project in your shell PATH environment variable to ensure you can use the cake command without providing the full path. An example of this is shown below – to add the CakePHP console directory to your path, enter:

export PATH=”$PATH:/path/to/cakephp/cake/console”

Once you’re done, you will be able to run the cake command without providing a full path to the executable. If you are going to be using CakePHP long-term, it’s handy to add this to your user profile, so that the path is modified, and cake is made available each time you create a new shell.

Baking the project files

Rather than manually creating all the files that are required for the library application we’re building, we’re going to take advantage of the CakePHP bake facility. This will generate all the files we need, and we can provide customisation from there. It’s a great way to get up and running in an extremely small amount of time.

Change into an appropriate web-accessible directory on your computer, for example:

cd /var/www/localhost

Bake the project skeleton with the following command:

cake bake project library

Lots of magic just happened. Trust me. But it’s not all a black box: you can have a look through the files that were generated, to gain an understanding of the basic app directory structure for CakePHP.

So take a moment to check out the files and structure that bake has created for you. It’s made a copy of the app structure that you see in the download (or clone) of CakePHP. Brilliant! There are a few differences to the standard app fileset to help you out. It’s modified some variables in web root/index.php to provide the path to your CakePHP directory. This means you can deploy and distribute apps without having to copy the CakePHP core around all the time! Double brilliant!

Configure the database connection

If you load up your application at this point, you will notice errors and warnings being indicated by yellow boxes in your browser. Its not the end of the world, and CakePHP tries to provide the best descriptions and useful error reports to get you going. These errors will be regarding database access, as we have not configured CakePHP to connect to our database yet. Let’s do this now. Change into your new projects directory with cd library. First up, we need to set up our database configuration so cake can find the database we want to use. Copy or move the file




Edit this file to change the values in the default connection to match those of your database. The test connection is used for unit testing, and can safely be removed or ignored for now. See my default configuration for example:

var $default = array(
   ‘driver’ => ‘mysql’,
   ‘persistent’ => false,
   ‘host’ => ‘localhost’,
   ‘login’ => ‘dev’,
   ‘password’ => ‘dev’,
   ‘database’ => ‘library’,
   ‘prefix’ => ‘’,

Let’s use bake again to save us a whole bunch of time. We’ll issue three quick commands to generate code for our models, views and controllers. This covers all the layers in our MVC paradigm. You can do this manually, but using bake this time around shows how much we can have CakePHP generate for us to save time, and to do repetitive tasks that would otherwise take much longer.

Slugs in my app!

If you are wondering what on earth a ‘slug’ is on the books table, its not a squishy slimy critter, it’s what we call a URL safe string based on the book’s name. We’ll be generating this automatically when users create boooks, behind the scenes.

Bake all the models:

cake bake model all

Bake all the controllers:

cake bake controller all

And finally, bake all the views:

cake bake view all

Review what’s been generated

Before pushing on too far, let’s take a moment to review what exactly was created for us in the last three console commands. Take another look at the code that has been generated for you. Not only has it created the basic models and common controller code required to get your app up and running in under two minutes, it’s identified that you want users associated with books through the conventional use of the user_id field on the book table. This allows the controllers to fetch associated data like books, when a user model is being queried. This is extremely helpful whenever you need to get hold of user information and display it quickly and easily.

Initial test drive

Once you have had a look through what’s been added in the models, controllers and views directories, it’s time to take this baby for a test drive. That’s right folks, the only code we have been required to delve into at this point is the database configuration, and that was just replacing a few values.

You'll be redirected back to the users list once you hit the Submit button, and the entry that you just added will be visible in the users list.

You'll be redirected back to the users list once you hit the Submit button, and the entry that you just added will be visible in the users list.

Browse to the location you deployed your app. You will be greeted with the default CakePHP install page, if you are presented with any warnings about database connection, you should resolve those in your config/database.php before continuing.

Let’s take a peek at what results we have so far by looking at the users controller. Browse to /users on your application. The URL will vary based on your deployment. Mine is http://localhost/library/users. You’ll see an empty list and some actions down the side.

Adding some books

It’s now time to get serious – we’ve got to add some books before we can call it a day. Fortunately, the functionality is already done for us, so we just need to customise it to make it even easier.

Including book titles in your URLs will increase search engine indexing results, making it easier for users to find what they want.

Including book titles in your URLs will increase search engine indexing results, making it easier for users to find what they want.

Click through the navigation on the left to add a new book. You’ll notice that your user is available in the drop-down list in the add book form. This information is automatically queried and used for you, because the database structure that we used for both the users and the books tables are following the CakePHP conventions. This means that CakePHP can use predictable queries to find and display information, saving you time.

Where are we now?

What you have now is a full library application that associates users with books that they own, and enables you to list, add, edit and delete any of the records. Pretty sweet for having written no code at all, huh? It’s not just generating a great user experience; the code has been generated so that you can modify it to suit your needs!

Our new user makes an appearance in the users list.

Our new user makes an appearance in the users list.

Introducing slugs

Now let’s do something a bit fancy. If you take notice of the URLs that you have been browsing to, they include ugly 36-character strings to identify records. Sure, that’s fine, it works, and we could deploy the application as is, but it’s nice to show a human-readable URL, and the search engines will love you for it.

Open up the books controller in library/controllers/books_controller.php, and look for the view action. Keep in mind that when I use the term action, it just means a public method on a controller class.

Rather than taking an ID as the parameter, we’re going to pass a slug and adjust the find query to suit. In order to do this, we need to change the method signature. Change the view method signature to the following:

public function view($slug = null) {
   // more code to come.

Next, we’re going to use some of the methods that CakePHP provides by default on your models. For each field that you have defined on your model, there is a find method to match it. This means that you can quickly and easily find data on any of your models without needing to add additional methods to change the way CakePHP returns results.

Since we are now using the slug to identify a record, we need to change the use of ID through the books controller to instead do lookups and handling using the slug string.

Customising your slugs

Its important to note that the default for the Inflector::slug method is to use underscores in the slug generation, but you can change this to be any character or set of characters that best suits your needs. This is done by providing a second value to the slug method, which will be the string that is used for replacements for invalid URL characters. This is another example of CakePHP providing a sensible default, but allowing customisation to override those defaults.

One of the major benefits of using a PHP framework like CakePHP for your applications is demonstrated here. We’re going to rely on the information passed by the user through the URL to do lookups on the database. This might at first seem like a bad idea due to the possibilities of SQL injection. However, CakePHP handles the security of database queries while you use the built in find() operations. This means that you can rely on the SQL that is generated to be safe, valid and correct.

We’re almost there! Let’s take a look at the special find type I mentioned. Since we have a slug to look up, CakePHP provides the method findBySlug on the mode. Simple! Your code can now be changed to look like the following:

function view($slug = null) {
   if (!$slug) {
      $this->Session->setFlash(__(‘Invalid book’, true));
      $this->redirect(array(‘action’ => ‘index’));
   $this->set(‘book’, $this->Book->findBySlug($slug));

Simple, your view action now works on slugs instead of IDs. Next, update the books index view in library/views/books/index.ctp to use the slug of the book instead of the ID when going to the view action. This is necessary because we changed the information that is required to look up entries, and therefore the index view needs to now pass the correct information to reflect the changes we made. Change this line:

<?php echo $this->Html->link(__(‘View’, true), array(‘action’ => ‘view’, $book[‘Book’][‘id’])); ?>

to the following:

<?php echo $this->Html->link(__(‘View’, true), array(‘action’ => ‘view’, $book[‘Book’][‘slug’])); ?>

The only difference being the slug index in the associative array $book that is used from the view.

Automated slugs!

We now have the slug in the URL. However, on our add form for books, the slug was being manually populated by the user, and it’s not very user-friendly to have to type out the book title more than once, even if the style is different. It would be super awesome if this field were filled automatically. Since the slug field just needs to contain the correct words, and we need those words to come from the title, we can automate this process in a before or after save operation, and remove this tedium from the users.

The slug has been generated for us automatically!

The slug has been generated for us automatically!

Implementing an afterSave or beforeSave enables us to inject functionality around a save operation. Add the following to your Book model in library/models/book.php:

public function beforeSave($options = array()) {
   $this->data[‘Book’][‘slug’] = Inflector::slug($this->data[‘Book’][‘title’]);
   return true;

With the heavy lifting being done by the model now, you can remove the slug input field from the view. Remove the following line from library/models/book.php:

echo $this->Form->input(‘slug’);

Go ahead and add another book to your application. Once this has been added, you will be redirected to the books index view, and you will see that the slug, which once required us to manually provide a value, is now automatically generated, and used for the books view page.

The book addition form is a bit larger, providing all the inputs for the book information.

The book addition form is a bit larger, providing all the inputs for the book information.

Wrapping up

The tutorial should have taken about 15 minutes to run through, and maybe 20 minutes to read. In this time, we’ve created a system allowing the structured and organised storage of books into a database, and linked to users in the system. If we were to create this with regular PHP, we’d have to additionally take care of database connectivity, security, correctly querying associated information from users to models, pagination, and all other aspects that CakePHP provides by default.

The sample code that is available on GitHub is capable of being extended and customised to build out your own project, and forms a good base application from which to learn.

We'll be following this up with even more CakePHP tutorials soon - stay tuned!

First published in Linux Format

First published in Linux Format magazine

You should follow us on or Twitter

Your comments

This is great!

Keep 'em comming! :)

Very good intuitive tutorial!

Thank you for this, it was a very well explained tutorial on nice functionality of CakePHP.


Love this tutorial. Very informative. Just a correction, the last step where we remove the line "echo $this->Form->input(‘slug’);" should be from library/views/edit.ctp and not library/models/book.php


What if the generated slug for two different records are the same? ex: book_1 and book_1 are two different record?

Bon-bons aliment began in

Bon-bons aliment began in the Appreciation States, has lift the diverse Hollywood actress aliment recipe, Britney Spears (Britney Spears), Lindsay Lohan (Lindsay Lohan) and Whitney Porter (Whitney Mooring)are enthusiasts column. Why it attracts so uncountable celebrities to participate with them? Prime, the designated sweets sustenance with a to a noteworthy compass simple, do not requisite to provender in the direction of this subgenus of eatables, no complex cooking steps do not prerequisite a recurring plan. The only look you need to do is to air aside the dinner, crush when desiring 35 Jelly Belly fudge (calories, 80 calories), a Haribo Starmix fudge (single 14 calories), some licorice (nearby 100 calories) . After some metre, the firmness authority is lightened. Nutritionist Carole Symons said: "confectionery existence is the pretext why the efficacy appear to be be more discernible, the most prime finish or plummeted because of compassionate calorie intake. Calorie reduced conglomeration in actuality limit

lvtrend.comlouboutins solds

>Almost {all women|all ladies|each woman|each lady} {have been in|will be in|have been around in|come in} {a situation|a scenario|a predicament|an issue} {at least once|at least one time|one or more times|at least} {in a|inside a|in the|within a} {lifetime|life time|life span|life-time} {when they have|whether they have|when they've|when they #file_links<C>\Users\Administrator\Desktop\D1\key.txt,1,N]

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Username:   Password: