CakePHP is a great rapid development framework, and the blog tutorial is a fine starting point on any journey to learn it. A tutorial covering a few more of the dirty realities of an actual project would have been a welcome next step in my first CakePHP encounter. This series aspires to being that next step. The project described here loosely corresponds to a small project we recently completed for a client here at NuRelm which means, hopefully, that it will include enough “aw man that wasn’t in the blog tutorial” spots to make it interesting.
These articles will stick to a format of stating requirements for the current programming iteration (fancy agile methodology talk for “the manageable chunk of project that we’ll work on next”) in a “what we’ll do” section, outlining the path we’ll take in a “how we’ll do it” section, then getting to work in a “let’s do it” section.
I’ll be keeping all the final code for each part of the tutorial under the following Github project:
https://github.com/nurelm/beyondtheblog
The final code after each part of the tutorial will be tagged something like “part1″ (which is the tag for this part’s final code) depending so that it will be easy to switch to different stages of our project’s development.
One more thing before we jump in. This tutorial is being written with a reader in mind who has a good understanding of the blog tutorial and all the trimmings that go with it. That reader has gotten her development machine’s Apache / PHP / MySQL groove on, and has no problems running the blog tutorial, making tables in MySQL, and so on. That same astute reader also has a working knowledge of MVC and understands how CakePHP’s convention-based approach negotiates a request. No problem, right? Ok, then, let’s dive in …
What We’ll Do
We are going to build a very simple order management system. The project is the sort that every Web developer knows. It’s almost simple enough that an email form with some tricks would work, but not quite. It’s weird enough that an out-of-the-box system will be a dirty mess to customize. It’s small enough that it should not get close to taking your client out of the four digit dollar range. And it’s definitely not a project that you want to bill 20 hours for, but spend 40 building.
This particular order system will, initially, just contain two model objects, a User and an Order. Most order systems will also contain some sort of Line Item object, but this project deals with simple orders that contain a lot of weird fields. So, just two objects for now, and we’ll worry about all those weird fields in a later article. Eventually, we’ll make the system email orders in the form of CSV files that can be consumed by another system that, for some reason, wants things in that format.
In this article, we’ll start by creating the model, then use CakePHP’s scaffolding and Bake to create corresponding views and controllers. As we progress through the series, we’ll work on a user workflow (introducing some handy view helpers), an admin workflow, authentication / authorization, validation, and view cleanup with layouts / view blocks / elements.
How We’ll Do It
Here are the steps we’ll be taking:
- Create our database and tables.
- Create corresponding models, complete with the associations we’ll need.
- Use dynamic scaffolding and take a quick look at what it generates.
- Use Bake to grab the results of that scaffolding and put them into files we can use as a starting point.
- Customize things.
Let’s Do It
Fire up a new CakePHP project structure, and we’ll be waiting right here, ready to dive right in and start by creating a database for use with this model.
Create our database and tables. Create a new MySQL database, use the name of your choice, and configure CakePHP’s app/Config/database.php file accordingly, then fire up the MySQL client of your choice and create the following tables / columns:
users: This table should have an id column (as should all CakePHP tables), created and modified columns (nice because CakePHP automagically keeps these updated), username and password fields (if you name them this way, our upcoming article on authentication will be aided by more CakePHP convention magics).
orders: This table should contain the following columns: id, created, modified, and user_id (the reason for this will become clear when we create our models next).
The SQL you use to generate these will look something like this:
CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) DEFAULT NULL, `created` datetime DEFAULT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB; CREATE TABLE `users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(45) NOT NULL, `password` varchar(45) NOT NULL, `created` datetime DEFAULT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username_UNIQUE` (`username`) ) ENGINE=InnoDB;
Create corresponding models: We’ll create two model files in /app/Model that should look very familiar, with a minor twist:
<?php
// app/Model/User.php
class User extends AppModel {
public $name = 'User';
public $hasMany = array(
'Order' => array(
'dependent' => true
)
);
}
and …
<?php
// app/Model/Order.php
class Order extends AppModel {
public $name = 'Order';
public $belongsTo = 'User';
}
Did you notice the $hasMany and $belongsTo variables sitting in our otherwise-normal model files? The define the relationship between Users and Orders … one User has many Orders that belong to it. If sprinkle this little bit of syntax in our model files, and stick to the convention of adding a column in our “child” table (Orders in this case) that tracks its parent ID, then CakePHP will do the rest, as we’ll see as we build our page. Check out the Cookbook’s section on associations for some details, but we’ve done all that we need for the purposes of this project.
Scaffolding. Now let’s do something fun and magical. Let’s get CakePHP to build the rest of our application. Sort of. To do that, create controllers for each of your model objects that look like this:
<?php
// app/Controller/UsersController.php
class UsersController extends AppController {
public $scaffold;
}
and
<?php
// app/Controller/OrdersController.php
class OrdersController extends AppController {
public $scaffold;
}
Then navigate to http://yourdevdomain/users and you should see something like this:

“What,” you say, “parlor tricks?!” Yes, you can pretty much do all the basic things that you need to do, but only the basics. Scaffolding is CakePHP’s way of allowing you to tinker with your model before nailing things down, but nobody is claiming it is something you can use for a production machine.
Go ahead and create a few users and orders. While you’re at it, notice some interesting details: First, notice that most columns are sortable. Then, after you create both users and orders, you’ll see that orders are sorted under the corresponding users (those associations we set up are working). You might also notice that some nice status messages are passed around to various screens after you perform actions. So even though this is far from a final application, a lot of niggling little details that make Web developers feel mundane, ugly and slow appear to be handled here, and one suspects that some form of heavenly cleverness lurks behind the scenes. And indeed there is. We’ll take a look at each of these mundane-crushing nuggets in later articles.
For now, wouldn’t it be ridiculously nice if we could fiddle with our model until we were happy with it using scaffolding to play with the results, then grab the code generated by scaffolding and use it as a starting point? Well it just so happens that you can.
Use Bake to generate initial code. A 20-project master of CakePHP might prefer hammering away at every project from scratch rather than bothering with a pre-baked starting point. But that starting point sure helps for the first few projects.
We’ll be using CakePHP’s Bake command line console. Windows users might have to do a little fiddling to get ready to use it, like so (thanks Melissa), while Linux and Mac users should be ready to roll right out of the gate. I’m writing this using a Linux machine for development, so any comments with tips for Windows or Macs would be appreciated.
Bake is easy to use. Fire up your command line tool of choice, change into your cake app’s root directory, and fire off the following command:
bash lib/Cake/Console/cake bake
If you’re on Windows, you’ll leave off the “bash” and probably use the “cake.bat” file instead of plain “cake” … but, again, any reader kind enough to offer hints in the comments on this will be rewarded by a warm virtual high-5. However you run it, the result is something like this:
[sms-home-desk] ~/Documents/cakephp > bash lib/Cake/Console/cake bake Welcome to CakePHP v2.0.5 Console --------------------------------------------------------------- App : app Path: /home/sms/Documents/cakephp/app/ --------------------------------------------------------------- Interactive Bake Shell --------------------------------------------------------------- [D]atabase Configuration [M]odel [V]iew [C]ontroller [P]roject [F]ixture [T]est case [Q]uit What would you like to Bake? (D/M/V/C/P/F/T/Q) >
I’ve only used Bake to build controllers and views. Models are so incredibly easy once your DB is set up and tables built that I’ve never tried that option. At this point go ahead and build both controllers. Here are the options I used for one controller (do this for both):
What would you like to Bake? (D/M/V/C/P/F/T/Q) > c --------------------------------------------------------------- Bake Controller Path: /home/sms/Documents/cakephp/app/Controller/ --------------------------------------------------------------- Use Database Config: (default/test) [default] > Possible Controllers based on your current database: 1. Orders 2. Users Enter a number from the list above, type in the name of another controller, or 'q' to exit [q] > 2 --------------------------------------------------------------- Baking UsersController --------------------------------------------------------------- Would you like to build your controller interactively? Warning: Choosing no will overwrite the UsersController. (y/n) [y] > Would you like to use dynamic scaffolding? (y/n) [n] > Would you like to create some basic class methods (index(), add(), view(), edit())? (y/n) [n] > y Would you like to create the basic class methods for admin routing? (y/n) [n] > n Would you like this controller to use other helpers besides HtmlHelper and FormHelper? (y/n) [n] > Would you like this controller to use any components? (y/n) [n] > Would you like to use Session flash messages? (y/n) [y] >
--------------------------------------------------------------- The following controller will be created: --------------------------------------------------------------- Controller Name: Users --------------------------------------------------------------- Look okay? (y/n) [y] >
Baking controller class for Users...
File `/home/sms/Documents/cakephp/app/Controller/UsersController.php` exists Do you want to overwrite? (y/n/q) [n] > y Wrote `/home/sms/Documents/cakephp/app/Controller/UsersController.php` PHPUnit is not installed. Do you want to bake unit test files anyway? (y/n) [y] > n
Then build views for both models. Here’s what I did for one set of views (do this for both):
What would you like to Bake? (D/M/V/C/P/F/T/Q) > v --------------------------------------------------------------- Bake View Path: /home/sms/Documents/cakephp/app/View/ --------------------------------------------------------------- Use Database Config: (default/test) [default] > Possible Controllers based on your current database: 1. Orders 2. Users Enter a number from the list above, type in the name of another controller, or 'q' to exit [q] > 2 Would you like bake to build your views interactively? Warning: Choosing no will overwrite Users views if it exist. (y/n) [n] > y Would you like to create some CRUD views (index, add, view, edit) for this controller? NOTE: Before doing so, you'll need to create your controller and model classes (including associated models). (y/n) [y] > Would you like to create the views for admin routing? (y/n) [n] > n
Baking `index` view file...
Creating file /home/sms/Documents/cakephp/app/View/Users/index.ctp Wrote `/home/sms/Documents/cakephp/app/View/Users/index.ctp`
Baking `view` view file...
Creating file /home/sms/Documents/cakephp/app/View/Users/view.ctp Wrote `/home/sms/Documents/cakephp/app/View/Users/view.ctp`
Baking `add` view file...
Creating file /home/sms/Documents/cakephp/app/View/Users/add.ctp Wrote `/home/sms/Documents/cakephp/app/View/Users/add.ctp`
Baking `edit` view file...
Creating file /home/sms/Documents/cakephp/app/View/Users/edit.ctp Wrote `/home/sms/Documents/cakephp/app/View/Users/edit.ctp` ---------------------------------------------------------------
View Scaffolding Complete.
Now go peek at your application again … it should look exactly the same as it did when we were using dynamic scaffolding. Then go look at your app/View and app/Controller directories, which should look very different, as in, they have things in them that were not there a minute ago. Bake created the views and controllers behind the scaffolding you saw earlier.
Take a few minutes to thoroughly explore what Bake just created, and make sure you are comfortable with everything going on within these new views and controllers. Try playing with them a bit to achieve various results, confident in the knowledge that you can easily recreate the whole mess with Bake again if you need to.
Customize things. I’ll leave most of the customizing to you, but let’s make one small change here together.
Right now, when I click on a user, the New Order button near the bottom of the screen does not automatically create an order associated with the current user. I feel that it should. Let’s change this behavior.
In order to do this, we will only need to make quick changes to the view pictured, along with the corresponding controller.
Let’s start with the view. All we want to do is to pass the current User’s ID onto the controller that will add the Order. Our URL is http://<yourdevbox>/users/view/1 so, due to CakePHP’s convention-based naming, we automatically know that our view will be located at app/View/Users/view.ctp. Upon inspecting that file, we can quickly locate the line of code that creates that button near the bottom of the file:
<li><?php echo $this->Html->link(__('New Order'), array('controller' => 'orders', 'action' => 'add'));?> </li>
Checking out CakePHP’s Html->link helper syntax, we find that adding one more element to that array allows us to pass a value along with the rest of the link, so we modify that line to use a new action and include our current User’s ID:
<li><?php echo $this->Html->link(__('New Order'), array('controller' => 'orders', 'action' => 'add_to_user', $user['User']['id']));?> </li>
One of the nice things about scaffolding / Bake for beginners is that it allows you to quickly steal commonly used code from other areas of the file you’re working on, which is a quick way to recall how to retrieve the current User’s ID in this case. Once you’re finished, save your file, reload it, and you should see that the “New Order” button now includes your User’s ID and is going to your new action … and will break if you click it since we haven’t created that new action yet.
Now, on to the controller. The URL that our newly rennovated “New Order” button points to is something like http://<yourdevbox>/orders/add_to_user/2, so we know the controller we’re after is going to be /app/Controllers/OrdersController, and the function we’ll need to create for that action will be called add_to_user. So, let’s create a function that takes a GET input, creates a new Order with our User’s ID, then sends us right back to where we started:
public function add_to_user($user_id) {
if (!$this->request->is('get')) {
throw new MethodNotAllowedException();
}
$this->Order->create();
if ($this->Order->save(array('user_id' => $user_id))) {
$this->Session->setFlash(__('The order has been saved'));
$this->redirect(array('controller' => 'users', 'action' => 'view', $user_id));
} else {
$this->Session->setFlash(__('The order could not be saved. Please, try again.'));
}
}
[Edit: As pointed out by mark in the comments, a GET isn't generally considered the way to go for an action that modifies data. We'll talk about why and remedy the situation in a later installment.]
You should now be able to add new orders to your User right from the User screen with wild abandon. Since our Orders do not yet contain any fields, we skipped the creation view altogether and just bounce the user right back to the User screen … see the redirect in that action above and figure out how it works.
Now go start working on the 40 other little changes you want to make to this app, and we’ll be back soon with another installment!













I don’t think using GET for a db change is a good thing for a beginners tutorial. those who start working with (cake)php might get the wrong ideas – now, that cake(2) is finally up to date according to HTTP specifications (using POSTs to modify the DB).
Oh, and you can drop the “public $name = …” stuff.
Other than that it looks fine
Hi Mark, thanks for the good feedback.
Agreed, the rule of thumb is that POSTs are used for “potentially unsafe” operations like writing something, and cleaning up such infractions (as well as securing such operations) will be good topics for future installments.
Since the Book insists of suggesting that $name be declared, I tried to stick to that here, although it does seem a little repetitive
Sam, Thanks for this tutorial. I am new to CakePHP (well, new to LAMP in general) and I had been struggling to get child records to show. (I had not used the scaffolding before!)
Yours was the first tutorial I have found which was clear, and makes sense. So thanks – you have saved me hours of torture.
Thanks Pav, I’m glad it was helpful.
- Sam
I was having trouble getting Bake to run on my XAMPP install on Windows 7 but found the solution:
You must invoke php.exe to open the cake.php file in your cakephp/lib/cake/console directory. Navigate to XAMPP php folder and open a command window there (or open command window and cd /d to the XAMPP php folder or add php directory to system path). Type “php /path/to/cake.php bake” (without quotes unless you have spaces in your path) and off you go.
By the way, thanks for this great tutorial!
Hey Ben, thanks for the tip and kind words, glad the tutorial was useful.
very nice tutorials can u please tell if we have two tables, user, order and acceptorder.
how can we save user_id, and order_id from the user and order table save into acceptorder table?