CakePHP Cookbook Documentation
Release 2.x

Cake Software Foundation

Mar 17, 2024

Contents

1

Getting Started
Blog Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Blog Tutorial - Adding a layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1
1
9

2

Installation
Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Downloading CakePHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Production . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Advanced Installation and URL Rewriting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Fire It Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29
29
30
30
30
31
31
32
32
36

3

CakePHP Overview
What is CakePHP? Why use it? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Understanding Model-View-Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Where to Get Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

37
37
38
40

4

Controllers
The App Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Request parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Controller actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Request Life-cycle callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Controller Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Controller Attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
More on controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

43
43
44
44
45
46
53
55

5

Views
View Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Using view blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

77
77
79
81
83
i

Creating your own view classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
View API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
More about Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

86
87
89

6

Models197
Understanding Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
More on models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199

7Core Libraries
General Purpose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Behaviors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

333
333
357
357
409
409

8

Plugins
How To Install Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
How To Use Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
How To Create Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

529
529
531
533

9

Shells, Tasks & Console Tools
The CakePHP console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Creating a shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Shell tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Invoking other shells from your shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Console output levels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Styling output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Configuring options and generating help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Routing in shells / CLI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Shell API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
More topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

539
539
541
543
544
544
545
546
553
553
556

10 Development
Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Routing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Sessions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Dispatcher Filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

575
575
589
606
612
618
621
624
648
652

11 Deployment
Check your security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Set document root . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Update core.php . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Improve your application’s performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

657
657
657
658
658

12 Tutorials & Examples
Blog Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Blog Tutorial - Adding a layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Simple Authentication and Authorization Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Simple Acl controlled Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Simple Acl controlled Application - part 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

659
659
662
673
681
687

ii

13 Contributing
Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Tickets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Coding Standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Backwards Compatibility Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
CakePHP Development Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

691
691
699
700
702
714
716

14 Appendices2.10 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.9 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.8 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.7 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.6 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.5 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.4 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.0 Migration Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Migration from 1.2 to 1.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
General Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

719
719
720
721
723
725
728
733
739
745
750
761
793
811

15 Indices and tables

813

Index

815

iii

iv

CHAPTER 1

Getting Started

The CakePHP framework provides a robust base for your application. It can handle every aspect, from the user’s initial
request all the way to the final rendering of a web page. And since the framework follows the principles of MVC, it
allows you to easily customize and extend most aspects of your application.
The framework also provides a basic organizational structure, from filenames to database table names, keeping yourentire application consistent and logical. This concept is simple but powerful. Follow the conventions and you’ll always
know exactly where things are and how they’re organized.
The best way to experience and learn CakePHP is to sit down and build something. To start off we’ll build a simple
blog application.

Blog Tutorial
Welcome to CakePHP. You’re probably checking out this tutorial because you want to learn more about how CakePHP
works. It’s our aim to increase productivity and make coding more enjoyable: we hope you’ll see this as you dive into
the code.
This tutorial will walk you through the creation of a simple blog application. We’ll be getting and installing CakePHP,
creating and configuring a database, and creating enough application logic to list, add, edit, and delete blog posts.
Here’s what you’ll need:
1. A running web server. We’re going to assume you’re using Apache, though the instructions for using otherservers should be very similar. We might have to play a little with the server configuration, but most folks can
get CakePHP up and running without any configuration at all. Make sure you have PHP 5.2.8 or greater.
2. A database server. We’re going to be using MySQL server in this tutorial. You’ll need to know enough about
SQL in order to create a database: CakePHP will be taking the reins from there. Since we’re using MySQL, also
make sure that you have pdo_mysql enabled in PHP.
3. Basic PHP knowledge. The more object-oriented programming you’ve done, the better: but fear not if you’re aprocedural fan.
1

CakePHP Cookbook Documentation, Release 2.x

4. Finally, you’ll need a basic knowledge of the MVC programming pattern. A quick overview can be found in
Understanding Model-View-Controller. Don’t worry, it’s only half a page or so.
Let’s get started!

Getting CakePHPFirst, let’s get a copy of fresh CakePHP code.
To get a fresh download, visit the CakePHP project on GitHub: https://github.com/cakephp/cakephp/tags and download
the latest release of 2.0
You can also clone the repository using git4 :git clone -b 2.x git://github.com/cakephp/cakephp.git
Regardless of how you downloaded it, place the code inside of your DocumentRoot. Once finished, your directory
setup should look something like the following:/path_to_document_root
/app
/lib
/plugins/vendors.htaccessindex.php
READMENow might be a good time to learn a bit about how CakePHP’s directory structure works: check out the CakePHP
Folder Structure section.
Tmp directory permissions
Next we’ll need to make the app/tmp directory writable by the webserver. The best way to do this is to find out whatuser your webserver runs as. You can run <?php echo exec('whoami'); ?> inside any PHP file your webserver
can execute. You should see a username printed. Change the ownership of the app/tmp directory to that user. The
final command you run (in *nix) might look something like this:$ chown -R www-data app/tmp
If for some reason CakePHP can’t write to that directory, you’ll see warnings and uncaught exceptions that cache datacannot be written.
4 https://git-scm.com/

2

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x

Creating the Blog Database
Next, let’s set up the underlying database for our blog. If you haven’t already done so, create an empty database for use
in this tutorial, with a name of your choice. Right now, we’ll just create a single table to store our posts. We’ll also
throw in a few posts right now to use for testing purposes. Execute the following SQL statements into your database:/* First, create our posts table: */CREATE TABLE posts (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(50),body TEXT,
created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL);/* Then insert some posts for testing: */INSERT INTO posts (title, body, created)
VALUES ('The title', 'This is the post body.', NOW());
INSERT INTO posts (title, body, created)
VALUES ('A title once again', 'And the post body follows.', NOW());
INSERT INTO posts (title, body, created)
VALUES ('Title strikes back', 'This is really exciting! Not.', NOW());
The choices on table and column names are not arbitrary. If you follow CakePHP’s database naming conventions, and
CakePHP’s class naming conventions (both outlined in CakePHP Conventions), you’ll be able to take advantage of alot of free functionality and avoid configuration. CakePHP is flexible enough to accommodate even the worst legacy
database schema, but adhering to convention will save you time.
Check out CakePHP Conventions for more information, but suffice it to say that naming our table ‘posts’ automatically hooks it to our Post model, and having fields called ‘modified’ and ‘created’ will be automagically managed by
CakePHP.

CakePHP Database Configuration
Onward and upward: let’s tell CakePHP where our database is and how to connect to it. For many, this is the first andlast time you configure anything.
A copy of CakePHP’s database configuration file is found in /app/Config/database.php.default. Make a copyof this file in the same directory, but name it database.php.
The config file should be pretty straightforward: just replace the values in the $default array with those that apply toyour setup. A sample completed configuration array might look something like the following:
public $default = array('datasource' => 'Database/Mysql','persistent' => false,'host' => 'localhost','port' => '','login' => 'cakeBlog','password' => 'c4k3-rUl3Z','database' => 'cake_blog_tutorial','schema' => '','prefix' => '','encoding' => 'utf8');

Blog Tutorial

3

CakePHP Cookbook Documentation, Release 2.x

Once you’ve saved your new database.php file, you should be able to open your browser and see the CakePHP
welcome page. It should also tell you that your database connection file was found, and that CakePHP can successfully
connect to the database.
Note: Remember that you’ll need to have PDO, and pdo_mysql enabled in your php.ini.

Optional Configuration
There are a few other items that can be configured. Most developers complete these laundry-list items, but they’re notrequired for this tutorial. One is defining a custom string (or “salt”) for use in security hashes. The second is defining
a custom number (or “seed”) for use in encryption.
The security salt is used for generating hashes. Change the default Security.salt value in /app/Config/core.php.
The replacement value should be long, hard to guess and be as random as you can make it:/*** A random string used in security hashing methods.
*/
Configure::write('Security.salt', 'pl345e-P45s_7h3*S@l7!');The cipher seed is used for encrypt/decrypt strings. Change the default Security.cipherSeed value by editing/app/Config/core.php. The replacement value should be a large random integer:/*** A random numeric string (digits only) used to encrypt/decrypt strings.
*/
Configure::write('Security.cipherSeed', '7485712659625147843639846751');

A Note on mod_rewrite
Occasionally new users will run into mod_rewrite issues. For example if the CakePHP welcome page looks a little
funny (no images or CSS styles), it probably means mod_rewrite is not functioning on your system. Please refer to oneof the sections below about URL rewriting for your webserver to get you up and running:
URL Rewriting
Apache and mod_rewrite (and .htaccess)While CakePHP is built to work with mod_rewrite out of the box–and usually does–we’ve noticed that a few users
struggle with getting everything to play nicely on their systems.
Here are a few things you might try to get it running correctly. First look at your httpd.conf. (Make sure you are editing
the system httpd.conf rather than a user- or site-specific httpd.conf.)
These files can vary between different distributions and Apache versions. You may also take a look at https://wiki.
apache.org/httpd/DistrosDefaultLayout for further information.
1. Make sure that an .htaccess override is allowed and that AllowOverride is set to All for the correct DocumentRoot.
You should see something similar to:4

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x# Each directory to which Apache has access can be configured with respect# to which services and features are allowed and/or disabled in that# directory (and its subdirectories).
## First, we configure the "default" to be a very restrictive set of# features.
#<Directory />Options FollowSymLinks
AllowOverride All#
Order deny,allow#
Deny from all</Directory>
For users having apache 2.4 and above, you need to modify the configuration file for your httpd.conf or virtual
host configuration to look like the following:<Directory /var/www/>
Options FollowSymLinks
AllowOverride All
Require all granted</Directory>
2. Make sure you are loading mod_rewrite correctly. You should see something like:
LoadModule rewrite_module libexec/apache2/mod_rewrite.so
In many systems these will be commented out by default, so you may just need to remove the leading # symbols.
After you make changes, restart Apache to make sure the settings are active.
Verify that your .htaccess files are actually in the right directories. Some operating systems treat files that startwith ‘.’ as hidden and therefore won’t copy them.
3. Make sure your copy of CakePHP comes from the downloads section of the site or our Git repository, and has
been unpacked correctly, by checking for .htaccess files.
CakePHP root directory (must be copied to your document; redirects everything to your CakePHP app):<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule^$ app/webroot/[L]
RewriteRule(.*) app/webroot/$1 [L]</IfModule>
CakePHP app directory (will be copied to the top directory of your application by bake):<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule^$
webroot/
RewriteRule(.*) webroot/$1</IfModule>

[L]
[L]

CakePHP webroot directory (will be copied to your application’s web root by bake):

Blog Tutorial

5

CakePHP Cookbook Documentation, Release 2.x<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]</IfModule>
If your CakePHP site still has problems with mod_rewrite, you might want to try modifying settings for Virtual
Hosts. On Ubuntu, edit the file /etc/apache2/sites-available/default (location is distribution-dependent). In thisfile, ensure that AllowOverride None is changed to AllowOverride All, so you have:<Directory />Options FollowSymLinks
AllowOverride All</Directory><Directory /var/www>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order Allow,Deny
Allow from all</Directory>
On Mac OSX, another solution is to use the tool virtualhostx5 to make a Virtual Host to point to your folder.
For many hosting services (GoDaddy, 1and1), your web server is actually being served from a user directory that
already uses mod_rewrite. If you are installing CakePHP into a user directory (http://example.com/~username/cakephp/), or any other URL structure that already utilizes mod_rewrite, you’ll need to add RewriteBase statements to the .htaccess files CakePHP uses (/.htaccess, /app/.htaccess, /app/webroot/.htaccess).
This can be added to the same section with the RewriteEngine directive, so for example, your webroot .htaccessfile would look like:<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /path/to/cake/app
RewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]</IfModule>
The details of those changes will depend on your setup, and can include additional things that are not related to
CakePHP. Please refer to Apache’s online documentation for more information.
4. (Optional) To improve production setup, you should prevent invalid assets from being parsed by CakePHP. Modify your webroot .htaccess to something like:<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /path/to/cake/app
RewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !^/(app/webroot/)?(img|css|js)/(.*)$
RewriteRule ^(.*)$ index.php [QSA,L]</IfModule>
5 https://clickontyler.com/virtualhostx/

6

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x

The above will simply prevent incorrect assets from being sent to index.php and instead display your webserver’s
404 page.
Additionally you can create a matching HTML 404 page, or use the default built-in CakePHP 404 by adding an
ErrorDocument directive:
ErrorDocument 404 /404-not-foundPretty URLs on nginx
nginx does not make use of .htaccess files like Apache, so it is necessary to create those rewritten URLs in the siteavailable configuration. Depending upon your setup, you will have to modify this, but at the very least, you will need
PHP running as a FastCGI instance.
server {listen80;
server_name www.example.com;
rewrite ^(.*) http://example.com$1 permanent;}
server {listen80;
server_name example.com;# root directive should be global
root/var/www/example.com/public/app/webroot/;index index.php;
access_log /var/www/example.com/log/access.log;
error_log /var/www/example.com/log/error.log;
location / {try_files $uri $uri/ /index.php?$args;}
location ~ \.php$ {try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_pass
127.0.0.1:9000;
fastcgi_index
index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;}}
If for some exotic reason you cannot change your root directory and need to run your project from a subfolder likeexample.com/subfolder/, you will have to inject “/webroot” in each request.
location ~ ^/(subfolder)/(.*)? {index index.php;
set $new_uri /$1/webroot/$2;
try_files $new_uri $new_uri/ /$1/index.php?$args;(continues on next page)

Blog Tutorial

7

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)... php handling ...
}Note: Recent configuration of PHP-FPM is set to listen to php-fpm socket instead of TCP port 9000 on address
127.0.0.1. If you get 502 bad gateway error from above configuration, try replacing fastcgi_pass from TCP port tosocket path (eg: fastcgi_pass unix:/var/run/php5-fpm.sock;).

URL Rewrites on IIS7 (Windows hosts)
IIS7 does not natively support .htaccess files. While there are add-ons that can add this support, you can also importhtaccess rules into IIS to use CakePHP’s native rewrites. To do this, follow these steps:
1. Use Microsoft’s Web Platform Installer6 to install the URL Rewrite Module 2.07 or download it directly (32-bit8/ 64-bit9 ).
2. Create a new file called web.config in your CakePHP root folder.
3. Using Notepad or any XML-safe editor, copy the following code into your new web.config file. . .
<?xml version="1.0" encoding="UTF-8"?><configuration><system.webServer><rewrite><rules><rule name="Rewrite requests to test.php"
stopProcessing="true"><match url="^test.php(.*)$" ignoreCase="false" /><action type="Rewrite" url="app/webroot/test.php{R:1}" /></rule><rule name="Exclude direct access to app/webroot/*"
stopProcessing="true"><match url="^app/webroot/(.*)$" ignoreCase="false" /><action type="None" /></rule><rule name="Rewrite routed access to assets(img, css, files, js, favicon)˓→"stopProcessing="true"><match url="^(img|css|files|js|favicon.ico)(.*)$" /><action type="Rewrite" url="app/webroot/{R:1}{R:2}"
appendQueryString="false" /></rule><rule name="Rewrite requested file/folder to index.php"
stopProcessing="true"><match url="^(.*)$" ignoreCase="false" /><action type="Rewrite" url="index.php"
appendQueryString="true" /></rule>(continues on next page)6 https://www.microsoft.com/web/downloads/platform.aspx
7 https://www.iis.net/downloads/microsoft/url-rewrite

8 https://www.microsoft.com/en-us/download/details.aspx?id=5747
9 https://www.microsoft.com/en-us/download/details.aspx?id=7435

8

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)</rules></rewrite></system.webServer></configuration>
Once the web.config file is created with the correct IIS-friendly rewrite rules, CakePHP’s links, CSS, JavaScipt, andrerouting should work correctly.
URL-Rewriting on lighttpdLighttpd does not support .htaccess functions, so you can remove all .htaccess files. In the lighttpd configuration, makesure you’ve activated “mod_rewrite”. Add a line:
url.rewrite-if-not-file =("^([^\?]*)(\?(.+))?$" => "/index.php?url=$1&$3")

URL rewrite rules for Hiawatha
The required UrlToolkit rule (for URL rewriting) to use CakePHP with Hiawatha is:
UrlToolkit {ToolkitID = cakephp
RequestURI exists Return
Match .* Rewrite /index.php}

I don’t / can’t use URL rewriting
If you don’t want to or can’t use URL rewriting on your webserver, refer to the core configuration.
Now continue to Blog Tutorial - Adding a layer to start building your first CakePHP application.

Blog Tutorial - Adding a layer
Create a Post Model
The Model class is the bread and butter of CakePHP applications. By creating a CakePHP model that will interact with
our database, we’ll have the foundation in place needed to do our view, add, edit, and delete operations later.
CakePHP’s model class files go in /app/Model, and the file we’ll be creating will be saved to /app/Model/Post.php.
The completed file should look like this:class Post extends AppModel {}Naming conventions are very important in CakePHP. By naming our model Post, CakePHP can automatically infer that
this model will be used in the PostsController, and will be tied to a database table called posts.

Blog Tutorial - Adding a layer

9CakePHP Cookbook Documentation, Release 2.x

Note: CakePHP will dynamically create a model object for you if it cannot find a corresponding file in /app/Model.
This also means that if you accidentally name your file wrong (for example, post.php or posts.php instead of Post.php),
CakePHP will not recognize any of your settings and will use the defaults instead.
For more on models, such as table prefixes, callbacks, and validation, check out the Models chapter of the Manual.

Create a Posts Controller
Next, we’ll create a controller for our posts. The controller is where all the controlling logic for post interaction will
happen. In a nutshell, it’s the place where you play with the models and get post-related work done. We’ll place thisnew controller in a file called PostsController.php inside the /app/Controller directory. Here’s what the basic
controller should look like:class PostsController extends AppController {public $helpers = array('Html', 'Form');}
Now, let’s add an action to our controller. Actions often represent a single function or interface in an application. For
example, when users request www.example.com/posts/index (which is the same as www.example.com/posts/), theymight expect to see a listing of posts. The code for that action would look something like this:class PostsController extends AppController {public $helpers = array('Html', 'Form');
public function index() {$this->set('posts', $this->Post->find('all'));}}By defining function index() in our PostsController, users can access the logic there by requestingwww.example.com/posts/index. Similarly, if we were to define a function called foobar(), users would be able toaccess that at www.example.com/posts/foobar.
Warning: You may be tempted to name your controllers and actions a certain way to obtain a certain URL. Resistthat temptation. Follow CakePHP conventions (capitalization, plural names, etc.) and create readable, understandable action names. You can map URLs to your code using “routes” covered later on.
The single instruction in the action uses set() to pass data from the controller to the view (which we’ll create next).
The line sets the view variable called ‘posts’ equal to the return value of the find('all') method of the Post model.
Our Post model is automatically available at $this->Post because we’ve followed CakePHP’s naming conventions.
To learn more about CakePHP’s controllers, check out the Controllers chapter.

10

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x

Creating Post Views
Now that we have our data flowing to our model, and our application logic and flow defined by our controller, let’screate a view for the index action we created above.
CakePHP views are just presentation-flavored fragments that fit inside an application’s layout. For most applications,they’re HTML mixed with PHP, but they may end up as XML, CSV, or even binary data.
A layout is presentation code that is wrapped around a view. Multiple layouts can be defined, and you can switch
between them, but for now, let’s just use the default.
Remember how in the last section we assigned the ‘posts’ variable to the view using the set() method? That would
pass data to the view that would look something like this:// print_r($posts) output:
Array([0] => Array([Post] => Array([id] => 1[title] => The title[body] => This is the post body.
[created] => 2008-02-13 18:34:55[modified] =>))[1] => Array([Post] => Array([id] => 2[title] => A title once again[body] => And the post body follows.
[created] => 2008-02-13 18:34:56[modified] =>))[2] => Array([Post] => Array([id] => 3[title] => Title strikes back[body] => This is really exciting! Not.
[created] => 2008-02-13 18:34:57[modified] =>)))CakePHP’s view files are stored in /app/View inside a folder named after the controller to which they correspond.
(We’ll have to create a folder named ‘Posts’ in this case.) To format this post data into a nice table, our view code mightlook something like thisBlog Tutorial - Adding a layer

11CakePHP Cookbook Documentation, Release 2.x<!-- File: /app/View/Posts/index.ctp --><h1>Blog posts</h1><table><tr><th>Id</th><th>Title</th><th>Created</th></tr><!-- Here is where we loop through our $posts array, printing out post info --><?php foreach ($posts as $post): ?><tr><td><?php echo $post['Post']['id']; ?></td><td><?php echo $this->Html->link($post['Post']['title'],array('controller' => 'posts', 'action' => 'view', $post['Post']['id'])); ?></td><td><?php echo $post['Post']['created']; ?></td></tr><?php endforeach; ?><?php unset($post); ?></table>You might have noticed the use of an object called $this->Html. This is an instance of the CakePHP HtmlHelperclass. CakePHP comes with a set of view helpers that make things like linking, form output, JavaScript and AJAX
a snap. You can learn more about how to use them in Helpers, but what’s important to note here is that the link()
method will generate an HTML link with the given title (the first parameter) and URL (the second parameter).
When specifying URLs in CakePHP, it is recommended that you use the array format. This is explained in moredetail in the section on Routes. Using the array format for URLs allows you to take advantage of CakePHP’s reverse routing capabilities. You can also specify URLs relative to the base of the application in the form of /controller/action/param1/param2.
At this point, you should be able to point your browser to http://www.example.com/posts/index. You should see yourview, correctly formatted with the title and table listing of the posts.
If you happened to have clicked on one of the links we created in this view (which link a post’s title to a URL/posts/view/some_id), you were probably informed by CakePHP that the action hadn’t yet been defined. If you were
not so informed, either something has gone wrong, or you actually did define it already, in which case you are verysneaky. Otherwise, we’ll create it in the PostsController now:// File: /app/Controller/PostsController.php
class PostsController extends AppController {public $helpers = array('Html', 'Form');
public function index() {$this->set('posts', $this->Post->find('all'));}public function view($id = null) {if (!$id) {throw new NotFoundException(__('Invalid post'));(continues on next page)

12

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}$post = $this->Post->findById($id);if (!$post) {throw new NotFoundException(__('Invalid post'));}$this->set('post', $post);}}
The set() call should look familiar. Notice we’re using findById() rather than find('all') because we only wanta single post’s information.
Notice that our view action takes a parameter: the ID of the post we’d like to see. This parameter is handed to the action
through the requested URL. If a user requests /posts/view/3, then the value ‘3’ is passed as $id.
We also do a bit of error checking to ensure that a user is actually accessing a record. If a user requests /posts/view,
we will throw a NotFoundException and let the CakePHP ErrorHandler take over. We also perform a similar checkto make sure the user has accessed a record that exists.
Now let’s create the view for our new ‘view’ action and place it in /app/View/Posts/view.ctp<!-- File: /app/View/Posts/view.ctp --><h1><?php echo h($post['Post']['title']); ?></h1><p><small>Created: <?php echo $post['Post']['created']; ?></small></p><p><?php echo h($post['Post']['body']); ?></p>
Verify that this is working by trying the links at /posts/index or manually requesting a post by accessing /posts/
view/1.

Adding Posts
Reading from the database and showing us the posts is a great start, but let’s allow for adding new posts.
First, start by creating an add() action in the PostsController:class PostsController extends AppController {public $helpers = array('Html', 'Form', 'Flash');public $components = array('Flash');public function index() {$this->set('posts', $this->Post->find('all'));}public function view($id) {if (!$id) {throw new NotFoundException(__('Invalid post'));}$post = $this->Post->findById($id);(continues on next page)

Blog Tutorial - Adding a layer

13CakePHP Cookbook Documentation, Release 2.x(continued from previous page)if (!$post) {throw new NotFoundException(__('Invalid post'));}$this->set('post', $post);}
public function add() {if ($this->request->is('post')) {$this->Post->create();if ($this->Post->save($this->request->data)) {$this->Flash->success(__('Your post has been saved.'));
return $this->redirect(array('action' => 'index'));}$this->Flash->error(__('Unable to add your post.'));}}}

Note: $this->request->is() takes a single argument, which can be the request METHOD (get, put, post,
delete) or some request identifier (ajax). It is not a way to check for specific posted data. For instance,$this->request->is('book') will not return true if book data was posted.

Note: You need to include the FlashComponent - and FlashHelper - in any controller where you will use it. If
necessary, include it in your AppController.
Here’s what the add() action does: if the HTTP method of the request was POST, it tries to save the data using the Post
model. If for some reason it doesn’t save, it just renders the view. This gives us a chance to show the user validation
errors or other warnings.
Every CakePHP request includes a CakeRequest object which is accessible using $this->request. The request
object contains useful information regarding the request that was just received, and can be used to control the flow ofyour application. In this case, we use the CakeRequest::is() method to check that the request is a HTTP POST
request.
When a user uses a form to POST data to your application, that information is available in $this->request->data.
You can use the pr() or debug() functions to print it out if you want to see what it looks like.
We use the FlashComponent’s FlashComponent::success() method to set a message to a session variable to be
displayed on the page after redirection. In the layout we have FlashHelper::render() which displays the message
and clears the corresponding session variable. The controller’s Controller::redirect function redirects to another
URL. The param array('action' => 'index') translates to URL /posts (that is, the index action of the posts
controller). You can refer to Router::url() function on the API10 to see the formats in which you can specify a URL
for various CakePHP functions.
Calling the save() method will check for validation errors and abort the save if any occur. We’ll discuss how thoseerrors are handled in the following sections.
We call the create() method first in order to reset the model state for saving new information. It does not actuallycreate a record in the database, but clears Model::$id and sets Model::$data based on your database field defaults.
10 https://api.cakephp.org

14

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x

Data Validation
CakePHP goes a long way toward taking the monotony out of form input validation. Everyone hates coding up endlessforms and their validation routines. CakePHP makes it easier and faster.
To take advantage of the validation features, you’ll need to use CakePHP’s FormHelper in your views. The FormHelper
is available by default to all views at $this->Form.
Here’s our add view:<!-- File: /app/View/Posts/add.ctp --><h1>Add Post</h1><?php
echo $this->Form->create('Post');echo $this->Form->input('title');echo $this->Form->input('body', array('rows' => '3'));echo $this->Form->end('Save Post');?>We use the FormHelper to generate the opening tag for an HTML form.
$this->Form->create() generates:

Here’s the HTML that<form id="PostAddForm" method="post" action="/posts/add">
If create() is called with no parameters supplied, it assumes you are building a form that submits via POST to thecurrent controller’s add() action (or edit() action when id is included in the form data).
The $this->Form->input() method is used to create form elements of the same name. The first parameter tells
CakePHP which field they correspond to, and the second parameter allows you to specify a wide array of options - inthis case, the number of rows for the textarea. There’s a bit of introspection and automagic here: input() will output
different form elements based on the model field specified.
The $this->Form->end() call generates a submit button and ends the form. If a string is supplied as the first parameterto end(), the FormHelper outputs a submit button named accordingly along with the closing form tag. Again, refer toHelpers for more on helpers.
Now let’s go back and update our /app/View/Posts/index.ctp view to include a new “Add Post” link. Before the<table>, add the following line:<?php echo $this->Html->link('Add Post',
array('controller' => 'posts', 'action' => 'add')); ?>You may be wondering: how do I tell CakePHP about my validation requirements? Validation rules are defined in themodel. Let’s look back at our Post model and make a few adjustments:
class Post extends AppModel {public $validate = array('title' => array('rule' => 'notBlank'),'body' => array('rule' => 'notBlank')(continues on next page)

Blog Tutorial - Adding a layer

15CakePHP Cookbook Documentation, Release 2.x(continued from previous page));}
The $validate array tells CakePHP how to validate your data when the save() method is called. Here, I’ve specified
that both the body and title fields must not be empty. CakePHP’s validation engine is strong, with a number of prebuilt rules (credit card numbers, email addresses, etc.) and flexibility for adding your own validation rules. For more
information, check the Data Validation.
Now that you have your validation rules in place, use the app to try to add a post with an empty title or body to see how
it works. Since we’ve used the FormHelper::input() method of the FormHelper to create our form elements, ourvalidation error messages will be shown automatically.

Editing Posts
Post editing: here we go. You’re a CakePHP pro by now, so you should have picked up a pattern. Make the action, thenthe view. Here’s what the edit() action of the PostsController would look like:public function edit($id = null) {if (!$id) {throw new NotFoundException(__('Invalid post'));}$post = $this->Post->findById($id);if (!$post) {throw new NotFoundException(__('Invalid post'));}if ($this->request->is(array('post', 'put'))) {$this->Post->id = $id;
if ($this->Post->save($this->request->data)) {$this->Flash->success(__('Your post has been updated.'));
return $this->redirect(array('action' => 'index'));}$this->Flash->error(__('Unable to update your post.'));}
if (!$this->request->data) {$this->request->data = $post;}}
This action first ensures that the user has tried to access an existing record. If they haven’t passed in an $id parameter,or the post does not exist, we throw a NotFoundException for the CakePHP ErrorHandler to take care of.
Next the action checks whether the request is either a POST or a PUT request. If it is, then we use the POST data to
update our Post record, or kick back and show the user validation errors.
If there is no data set to $this->request->data, we simply set it to the previously retrieved post.
The edit view might look something like this:<!-- File: /app/View/Posts/edit.ctp -->(continues on next page)

16

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)<h1>Edit Post</h1><?php
echo $this->Form->create('Post');echo $this->Form->input('title');echo $this->Form->input('body', array('rows' => '3'));echo $this->Form->input('id', array('type' => 'hidden'));echo $this->Form->end('Save Post');?>This view outputs the edit form (with the values populated), along with any necessary validation error messages.
One thing to note here: CakePHP will assume that you are editing a model if the ‘id’ field is present in the data array. If
no ‘id’ is present (look back at our add view), CakePHP will assume that you are inserting a new model when save()is called.
You can now update your index view with links to edit specific posts:<!-- File: /app/View/Posts/index.ctp(edit links added) --><h1>Blog posts</h1><p><?php echo $this->Html->link("Add Post", array('action' => 'add')); ?></p><table><tr><th>Id</th><th>Title</th><th>Action</th><th>Created</th></tr><!-- Here's where we loop through our $posts array, printing out post info --><?php foreach ($posts as $post): ?><tr><td><?php echo $post['Post']['id']; ?></td>
<td>
<?php
echo $this->Html->link($post['Post']['title'],array('action' => 'view', $post['Post']['id'])
);
?>
</td>
<td>
<?php
echo $this->Html->link('Edit',
array('action' => 'edit', $post['Post']['id'])
);
?>
</td>
<td>
<?php echo $post['Post']['created']; ?></td>(continues on next page)

Blog Tutorial - Adding a layer

17CakePHP Cookbook Documentation, Release 2.x(continued from previous page)</tr><?php endforeach; ?></table>Deleting Posts
Next, let’s make a way for users to delete posts. Start with a delete() action in the PostsController:
public function delete($id) {if ($this->request->is('get')) {throw new MethodNotAllowedException();}if ($this->Post->delete($id)) {$this->Flash->success(__('The post with id: %s has been deleted.', h($id)));} else {$this->Flash->error(__('The post with id: %s could not be deleted.', h($id)));}
return $this->redirect(array('action' => 'index'));}This logic deletes the post specified by $id, and uses $this->Flash->success() to show the user a confirmation
message after redirecting them on to /posts. If the user attempts to do a delete using a GET request, we throw an
Exception. Uncaught exceptions are captured by CakePHP’s exception handler, and a nice error page is displayed.
There are many built-in Exceptions that can be used to indicate the various HTTP errors your application might needto generate.
Because we’re just executing some logic and redirecting, this action has no view. You might want to update your index
view with links that allow users to delete posts, however:<!-- File: /app/View/Posts/index.ctp --><h1>Blog posts</h1><p><?php echo $this->Html->link('Add Post', array('action' => 'add')); ?></p><table><tr><th>Id</th><th>Title</th><th>Actions</th><th>Created</th></tr><!-- Here's where we loop through our $posts array, printing out post info --><?php foreach ($posts as $post): ?><tr>(continues on next page)

18

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)<td><?php echo $post['Post']['id']; ?></td>
<td>
<?php
echo $this->Html->link($post['Post']['title'],array('action' => 'view', $post['Post']['id'])
);
?>
</td>
<td>
<?php
echo $this->Form->postLink('Delete',array('action' => 'delete', $post['Post']['id']),array('confirm' => 'Are you sure?')
);
?>
<?php
echo $this->Html->link('Edit', array('action' => 'edit', $post['Post']['id'])
);
?>
</td>
<td>
<?php echo $post['Post']['created']; ?>
</td>
</tr>
<?php endforeach; ?></table>Using postLink() will create a link that uses JavaScript to do a POST request to delete our post. Allowing content
to be deleted using GET requests is dangerous, as web crawlers could accidentally delete all your content.
Note: This view code also uses the FormHelper to prompt the user with a JavaScript confirmation dialog before theyattempt to delete a post.

RoutesFor some, CakePHP’s default routing works well enough. Developers who are sensitive to user-friendliness and generalsearch engine compatibility will appreciate the way that CakePHP’s URLs map to specific actions. So we’ll just makea quick change to routes in this tutorial.
For more information on advanced routing techniques, see Routes Configuration.
By default, CakePHP responds to a request for the root of your site (e.g., http://www.example.com) using its PagesController, rendering a view called “home”. Instead, we’ll replace this with our PostsController by creating a routing rule.
CakePHP’s routing is found in /app/Config/routes.php. You’ll want to comment out or remove the line that defines
the default root route. It looks like this:Blog Tutorial - Adding a layer

19CakePHP Cookbook Documentation, Release 2.x

Router::connect('/',array('controller' => 'pages', 'action' => 'display', 'home'));
This line connects the URL ‘/’ with the default CakePHP home page. We want it to connect with our own controller,
so replace that line with this one:
Router::connect('/', array('controller' => 'posts', 'action' => 'index'));This should connect users requesting ‘/’ to the index() action of our PostsController.
Note: CakePHP also makes use of ‘reverse routing’. If, with the above route defined, you pass array('controller'=> 'posts', 'action' => 'index') to a function expecting an array, the resulting URL used will be ‘/’. It’s
therefore a good idea to always use arrays for URLs, as this means your routes define where a URL goes, and also
ensures that links point to the same place.

Conclusion
Creating applications this way will win you peace, honor, love, and money beyond even your wildest fantasies. Simple,
isn’t it? Keep in mind that this tutorial was very basic. CakePHP has many more features to offer, and is flexible in ways
we didn’t wish to cover here for simplicity’s sake. Use the rest of this manual as a guide for building more feature-richapplications.
Now that you’ve created a basic CakePHP application, you’re ready for the real thing. Start your own project and readthe rest of the Cookbook and API11 .
If you need help, there are many ways to get the help you need - please see the Where to Get Help page. Welcome to
CakePHP!
Suggested Follow-up Reading
These are common tasks people learning CakePHP usually want to study next:
1. Layouts: Customizing your website layout
2. Elements: Including and reusing view snippets
3. Scaffolding: Prototyping before creating code
4. Code Generation with Bake: Generating basic CRUD code
5. Simple Authentication and Authorization Application: User authentication and authorization tutorial
11 https://api.cakephp.org

20

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x

Additional Reading
A Typical CakePHP Request
We’ve covered the basic ingredients in CakePHP, so let’s look at how objects work together to complete a basic request.
Continuing with our original request example, let’s imagine that our friend Ricardo just clicked on the “Buy A Custom
Cake Now!” link on a CakePHP application’s landing page.

Fig. 1: Flow diagram showing a typical CakePHP request
Figure: 2. Typical CakePHP Request.
Black = required element, Gray = optional element, Blue = callback
1. Ricardo clicks the link pointing to http://www.example.com/cakes/buy, and his browser makes a request to yourweb server.
2. The Router parses the URL in order to extract the parameters for this request: the controller, action, and anyother arguments that will affect the business logic during this request.
3. Using routes, a request URL is mapped to a controller action (a method in a specific controller class). In thiscase, it’s the buy() method of the CakesController. The controller’s beforeFilter() callback is called before any
controller action logic is executed.
4. The controller may use models to gain access to the application’s data. In this example, the controller uses a model
to fetch Ricardo’s last purchases from the database. Any applicable model callbacks, behaviors, and DataSources
may apply during this operation. While model usage is not required, all CakePHP controllers initially require atleast one model.
5. After the model has retrieved the data, it is returned to the controller. Model callbacks may apply.

Blog Tutorial - Adding a layer

21CakePHP Cookbook Documentation, Release 2.x

6. The controller may use components to further refine the data or perform other operations (session manipulation,authentication, or sending emails, for example).
7. Once the controller has used models and components to prepare the data sufficiently, that data is handed to theview using the controller’s set() method. Controller callbacks may be applied before the data is sent. The viewlogic is performed, which may include the use of elements and/or helpers. By default, the view is rendered inside
a layout.
8. Additional controller callbacks (like afterFilter) may be applied. The complete, rendered view code is sent
to Ricardo’s browser.
CakePHP Conventions
We are big fans of convention over configuration. While it takes a bit of time to learn CakePHP’s conventions, you savetime in the long run: by following convention, you get free functionality, and you free yourself from the maintenancenightmare of tracking config files. Convention also makes for a very uniform system development, allowing otherdevelopers to jump in and help more easily.
CakePHP’s conventions have been distilled from years of web development experience and best practices. While wesuggest you use these conventions while developing with CakePHP, we should mention that many of these tenets areeasily overridden – something that is especially handy when working with legacy systems.
Controller Conventions
Controller class names are plural, CamelCased, and end in Controller.
LatestArticlesController are both examples of conventional controller names.

PeopleController and

The first method you write for a controller might be the index() method. When a request specifies a controller but not
an action, the default CakePHP behavior is to execute the index() method of that controller. For example, a request
for http://www.example.com/apples/ maps to a call on the index() method of the ApplesController, whereas
http://www.example.com/apples/view/ maps to a call on the view() method of the ApplesController.
You can also change the visibility of controller methods in CakePHP by prefixing controller method names with underscores. If a controller method has been prefixed with an underscore, the method will not be accessible directly fromthe web but is available for internal use. For example:
class NewsController extends AppController {public function latest() {$this->_findNewArticles();}
protected function _findNewArticles() {// Logic to find latest news articles}}
While the page http://www.example.com/news/latest/ would be accessible to the user as usual, someone trying to getto the page http://www.example.com/news/_findNewArticles/ would get an error, because the method is preceded withan underscore. You can also use PHP’s visibility keywords to indicate whether or not a method can be accessed froma URL. Non-public methods cannot be accessed.

22

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x

URL Considerations for Controller Names
As you’ve just seen, single word controllers map easily to a simple lower case URL path. For example,
ApplesController (which would be defined in the file name ‘ApplesController.php’) is accessed from http://example.
com/apples.
Multiple word controllers can be any ‘inflected’ form which equals the controller name so:• /redApples• /RedApples• /Red_apples• /red_apples
will all resolve to the index of the RedApples controller. However, the convention is that your URLs are lowercase
and underscored, therefore /red_apples/go_pick is the correct form to access the RedApplesController::go_pick
action.
For more information on CakePHP URLs and parameter handling, see Routes Configuration. If you havefiles/directories in your /webroot directory that share a name with one of your routes/controllers, you will be directedto the file/directory and, not to your controller.
File and Class Name Conventions
In general, filenames match the class names, which are CamelCased. So if you have a class MyNiftyClass, then in
CakePHP, the file should be named MyNiftyClass.php. Below are examples of how to name the file for each of thedifferent types of classes you would typically use in a CakePHP application:• The Controller class KissesAndHugsController would be found in a file named KissesAndHugsController.php• The Component class MyHandyComponent would be found in a file named MyHandyComponent.php• The Model class OptionValue would be found in a file named OptionValue.php• The Behavior class EspeciallyFunkableBehavior would be found in a file named EspeciallyFunkableBehavior.php• The View class SuperSimpleView would be found in a file named SuperSimpleView.php• The Helper class BestEverHelper would be found in a file named BestEverHelper.php
Each file would be located in the appropriate folder in your app folder.
Model and Database Conventions
Model class names are singular and CamelCased. Person, BigPerson, and ReallyBigPerson are all examples of conventional model names.
Table names corresponding to CakePHP models are plural and underscored. The underlying tables for the above
mentioned models would be people, big_people, and really_big_people, respectively.
You can use the utility library Inflector to check the singular/plural of words. See the Inflector for more information.
Field names with two or more words are underscored: first_name.
Foreign keys in hasMany, belongsTo or hasOne relationships are recognized by default as the (singular) name of therelated table followed by _id. So if a Baker hasMany Cake, the cakes table will refer to the bakers table via a baker_id

Blog Tutorial - Adding a layer

23

CakePHP Cookbook Documentation, Release 2.x

foreign key. For a table like category_types whose name contains multiple words, the foreign key would be category_type_id.
Join tables, used in hasAndBelongsToMany (HABTM) relationships between models, must be named after the modeltables they will join, e.g. users HABTM groups would be joined by groups_users, and should be arranged in alphabetical order, e.g. apes_zoos is better than zoos_apes.
All tables with which CakePHP models interact (with the exception of join tables) require a singular primary key
to uniquely identify each row. If you wish to model a table that does not already have a single-field primary key,
CakePHP’s convention is that a single-field primary key is added to the table. You must add a single-field primary key
if you want to use that table’s model.
If primary key’s name is not id, then you must set the Model.primaryKey attribute.
CakePHP does not support composite primary keys. If you want to directly manipulate your join table data, use direct
query calls or add a primary key to act on it as a normal model. For example:
CREATE TABLE posts_tags (id INT(10) NOT NULL AUTO_INCREMENT,
post_id INT(10) NOT NULL,
tag_id INT(10) NOT NULL,PRIMARY KEY(id));
Rather than using an auto-increment key as the primary key, you may also use char(36). CakePHP will then use aunique 36 character UUID (String::uuid) whenever you save a new record using the Model::save method.
View Conventions
View template files are named after the controller functions they display, in an underscored form. The getReady()function of the PeopleController class will look for a view template in /app/View/People/get_ready.ctp.
The basic pattern is /app/View/Controller/underscored_function_name.ctp.
By naming the pieces of your application using CakePHP conventions, you gain functionality without the hassle andmaintenance tethers of configuration. Here’s a final example that ties the conventions together:• Database table: “people”• Model class: “Person”, found at /app/Model/Person.php• Controller class: “PeopleController”, found at /app/Controller/PeopleController.php• View template, found at /app/View/People/index.ctp
Using these conventions, CakePHP knows that a request to http://example.com/people/ maps to a call on the index()
function of the PeopleController, where the Person model is automatically available (and automatically tied to the‘people’ table in the database), and renders to a file. None of these relationships have been configured by any meansother than by creating classes and files that you’d need to create anyway.
Now that you’ve been introduced to CakePHP’s fundamentals, you might try a run through the Blog Tutorial to see
how things fit together.

24

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x

CakePHP Folder Structure
After you’ve downloaded and extracted CakePHP, these are the files and folders you should see:
• app
• lib
• vendors• plugins• .htaccess• index.php• README
You’ll notice three main folders:• The app folder will be where you work your magic: it’s where your application’s files will be placed.
• The lib folder is where we’ve worked our magic. Make a personal commitment not to edit files in this folder.
We can’t help you if you’ve modified the core. Instead, look into modifying Application Extensions.
• Finally, the vendors folder is where you’ll place third-party PHP libraries you need to use with your CakePHP
applications.
The App Folder
CakePHP’s app folder is where you will do most of your application development. Let’s look a little closer at the folders
inside app.
ConfigHolds the (few) configuration files CakePHP uses. Database connection details, bootstrapping, core configuration
files and more should be stored here.
ConsoleContains the console commands and console tasks for your application. This directory can also contain a
Templates directory to customize the output of bake. For more information see Shells, Tasks & Console Tools.
Controller
Contains your application’s controllers and their components.
Lib
Contains libraries that do not come from 3rd parties or external vendors. This allows you to separate yourorganization’s internal libraries from vendor libraries.
LocaleStores string files for internationalization.
Model
Contains your application’s models, behaviors, and datasources.
PluginContains plugin packages.
Test
This directory contains all the test cases and test fixtures for your application. The Test/Case directory should
mirror your application and contain one or more test cases per class in your application. For more informationon test cases and test fixtures, refer to the Testing documentation.

Blog Tutorial - Adding a layer

25CakePHP Cookbook Documentation, Release 2.x

tmp
This is where CakePHP stores temporary data. The actual data it stores depends on how you have CakePHP
configured, but this folder is usually used to store model descriptions, logs, and sometimes session information.
Make sure that this folder exists and is writable, or the performance of your application will be severely impacted.
In debug mode, CakePHP will warn you if the folder is absent or not writable.
VendorAny third-party classes or libraries should be placed here. Doing so makes them easy to access using the
App::import(‘vendor’, ‘name’) function. Keen observers will note that this seems redundant, as there is alsoa vendors folder at the top level of our directory structure. We’ll get into the differences between the two when
we discuss managing multiple applications and more complex system setups.
View
Presentational files are placed here: elements, error pages, helpers, layouts, and view files.
webroot
In a production setup, this folder should serve as the document root for your application. Folders here also serve
as holding places for CSS stylesheets, images, and JavaScript files.
CakePHP Structure
CakePHP features Controller, Model, and View classes, but it also features some additional classes and objects thatmake development in MVC a little quicker and more enjoyable. Components, Behaviors, and Helpers are classes thatprovide extensibility and reusability to quickly add functionality to the base MVC classes in your applications. Right
now we’ll stay at a higher level, so look for the details on how to use these tools later on.
Application Extensions
Controllers, helpers and models each have a parent class you can use to define application-wide changes. AppController(located at /app/Controller/AppController.php), AppHelper (located at /app/View/Helper/AppHelper.
php) and AppModel (located at /app/Model/AppModel.php) are great places to put methods you want to sharebetween all controllers, helpers or models.
Although routes aren’t classes or files, they play a role in requests made to CakePHP. Route definitions tell CakePHP
how to map URLs to controller actions. The default behavior assumes that the URL /controller/action/var1/
var2 maps to Controller::action($var1, $var2), but you can use routes to customize URLs and how they are interpretedby your application.
Some features in an application merit packaging as a whole. A plugin is a package of models, controllers and views
that accomplishes a specific purpose that can span multiple applications. A user management system or a simplified
blog might be a good fit for CakePHP plugins.
Controller Extensions (“Components”)
A Component is a class that aids in controller logic. If you have some logic you want to share between controllers (orapplications), a component is usually a good fit. As an example, the core EmailComponent class makes creating andsending emails a snap. Rather than writing a controller method in a single controller that performs this logic, you canpackage the logic so it can be shared.
Controllers are also fitted with callbacks. These callbacks are available for your use, just in case you need to insert
some logic between CakePHP’s core operations. Callbacks available include:• beforeFilter(), executed before any controller action logic• afterFilter(), executed after all controller logic, including the rendering of the view
26

Chapter 1. Getting Started

CakePHP Cookbook Documentation, Release 2.x• beforeRender(), executed after controller logic, but before the view is rendered
Model Extensions (“Behaviors”)
Similarly, Behaviors work as ways to add common functionality between models. For example, if you store user data
in a tree structure, you can specify your User model as behaving like a tree, and gain free functionality for removing,
adding, and shifting nodes in your underlying tree structure.
Models are also supported by another class called a DataSource. DataSources are an abstraction that enable models
to manipulate different types of data consistently. While the main source of data in a CakePHP application is often
a database, you might write additional DataSources that allow your models to represent RSS feeds, CSV files, LDAP
entries, or iCal events. DataSources allow you to associate records from different sources: rather than being limited to
SQL joins, DataSources allow you to tell your LDAP model that it is associated with many iCal events.
Like controllers, models have callbacks:• beforeFind()• afterFind()• beforeValidate()• afterValidate()• beforeSave()• afterSave()• beforeDelete()• afterDelete()
The names of these methods should be descriptive enough to let you know what they do. You can find the details in
the models chapter.
View Extensions (“Helpers”)
A Helper is a class that aids in view logic. Much like a component used among controllers, helpers allow presentational
logic to be accessed and shared between views. One of the core helpers, JsHelper, makes AJAX requests within views
much easier and comes with support for jQuery (default), Prototype and Mootools.
Most applications have pieces of view code that are used repeatedly. CakePHP facilitates view code reuse with layouts
and elements. By default, every view rendered by a controller is placed inside a layout. Elements are used when small
snippets of content need to be reused in multiple views.

Blog Tutorial - Adding a layer

27

CakePHP Cookbook Documentation, Release 2.x

28

Chapter 1. Getting Started

CHAPTER 2

Installation

CakePHP is fast and easy to install. The minimum requirements are a webserver and a copy of CakePHP, that’s it!
While this manual focuses primarily on setting up on Apache (because it’s the most commonly used), you can configure
CakePHP to run on a variety of web servers such as lighttpd or Microsoft IIS.

Requirements• HTTP Server. For example: Apache. mod_rewrite is preferred, but by no means required.
• PHP 5.3.0 or greater (CakePHP version 2.6 and below support PHP 5.2.8 and above). CakePHP version 2.8.0
and above support PHP 7. To use PHP above 7.1 you may need to install mcrypt via PECL. See Security formore information.
Technically a database engine isn’t required, but we imagine that most applications will utilize one. CakePHP supports
a variety of database storage engines:• MySQL (4 or greater)• PostgreSQL• Microsoft SQL Server• SQLite
Note: All built-in drivers require PDO. You should make sure you have the correct PDO extensions installed.

Warning: CakePHP 2.x is end-of-life and in maintenance mode (critical bugfixes only). As such, functionality
added in PHP 7.1+ functionality is not well supported. You will likely have to disable deprecations using (E_ALL &~E_DEPRECATED & ~E_USER_DEPRECATED). We recommended you run PHP 5.6 or greater for your applications.
Earlier PHP versions are end-of-life, and provide an increased security risk.

29CakePHP Cookbook Documentation, Release 2.x

LicenseCakePHP is licensed under the MIT license. This means that you are free to modify, distribute and republish the sourcecode on the condition that the copyright notices are left intact. You are also free to incorporate CakePHP into any
commercial or closed source application.

Downloading CakePHP
There are two main ways to get a fresh copy of CakePHP. You can either download an archived copy (zip/tar.gz/tar.bz2)
from the main website, or check out the code from the git repository.
To download the latest major release of CakePHP, visit the main website https://cakephp.org and follow the “Download”
link.
All current releases of CakePHP are hosted on GitHub12 . GitHub houses both CakePHP itself as well as many otherplugins for CakePHP. The CakePHP releases are available at GitHub tags13 .
Alternatively you can get fresh off the press code, with all the bug-fixes and up to the minute enhancements. These canbe accessed from GitHub by cloning the GitHub14 repository:
git clone -b 2.x git://github.com/cakephp/cakephp.git

Permissions
CakePHP uses the app/tmp directory for a number of different operations. A few examples would be Model descriptions, cached views and session information.
As such, make sure the directory app/tmp and all its subdirectories in your CakePHP installation are writable by the
web server user.
One common issue is that the app/tmp directories and subdirectories must be writable both by the web server and thecommand line user. On a UNIX system, if your web server user is different from your command line user, you can run
the following commands just once in your project to ensure that permissions will be setup properly:
HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root␣˓→| head -1 | cut -d\-f1`
setfacl -R -m u:${HTTPDUSER}:rwx app/tmp
setfacl -R -d -m u:${HTTPDUSER}:rwx app/tmp
12 https://github.com/cakephp/cakephp

13 https://github.com/cakephp/cakephp/tags
14 https://github.com/cakephp/cakephp

30

Chapter 2. Installation

CakePHP Cookbook Documentation, Release 2.x

Setup
Setting up CakePHP can be as simple as slapping it in your web server’s document root, or as complex and flexible
as you wish. This section will cover the three main installation types for CakePHP: development, production, andadvanced.
• Development: easy to get going, URLs for the application include the CakePHP installation directory name, andless secure.
• Production: Requires the ability to configure the web server’s document root, clean URLs, very secure.
• Advanced: With some configuration, allows you to place key CakePHP directories in different parts of the filesystem, possibly sharing a single CakePHP core library folder amongst many CakePHP applications.

Development
A development installation is the fastest method to setup CakePHP. This example will help you install a CakePHP
application and make it available at http://www.example.com/cake_2_0/. We assume for the purposes of this example
that your document root is set to /var/www/html.
Unpack the contents of the CakePHP archive into /var/www/html. You now have a folder in your document root
named after the release you’ve downloaded (e.g. cake_2.0.0). Rename this folder to cake_2_0. Your development
setup will look like this on the file system:/var/www/html/
cake_2_0/
app/
lib/
plugins/
vendors/.htaccessindex.php
README
If your web server is configured correctly, you should now find your CakePHP application accessible at
http://www.example.com/cake_2_0/.

Using one CakePHP Checkout for multiple Applications
If you are developing a number of applications, it often makes sense to have them share the same CakePHP corecheckout. There are a few ways in which you can accomplish this. Often the easiest is to use PHP’s include_path.
To start off, clone CakePHP into a directory. For this example, we’ll use /home/mark/projects:
git clone -b 2.x git://github.com/cakephp/cakephp.git /home/mark/projects/cakephp
This will clone CakePHP into your /home/mark/projects directory. If you don’t want to use git, you can download
a zipball and the remaining steps will be the same. Next you’ll have to locate and modify your php.ini. On *nix
systems this is often in /etc/php.ini, but using php -i and looking for ‘Loaded Configuration File’, you can findthe actual location. Once you’ve found the correct ini file, modify the include_path configuration to include /home/mark/projects/cakephp/lib. An example would look like:include_path = .:/home/mark/projects/cakephp/lib:/usr/local/php/lib/php

Setup

31

CakePHP Cookbook Documentation, Release 2.x

After restarting your webserver, you should see the changes reflected in phpinfo().
Note: If you are on Windows, separate include paths with ; instead of :Having finished setting up your include_path your applications should be able to find CakePHP automatically.

Production
A production installation is a more flexible way to setup CakePHP. Using this method allows an entire domain to act asa single CakePHP application. This example will help you install CakePHP anywhere on your filesystem and make itavailable at http://www.example.com. Note that this installation may require the rights to change the DocumentRooton Apache webservers.
Unpack the contents of the CakePHP archive into a directory of your choice. For the purposes of this example, we
assume you chose to install CakePHP into /cake_install. Your production setup will look like this on the filesystem:/cake_install/app/
webroot/ (this directory is set as the ``DocumentRoot``
directive)lib/plugins/
vendors/.htaccessindex.php
README
Developers using Apache should set the DocumentRoot directive for the domain to:DocumentRoot /cake_install/app/webrootIf your web server is configured correctly, you should now find your CakePHP application accessible at http://www.
example.com.

Advanced Installation and URL Rewriting
Advanced Installation
Installing CakePHP with PEAR Installer
CakePHP publishes a PEAR package that you can install using the PEAR installer. Installing with the PEAR installer
can simplify sharing CakePHP libraries across multiple applications. To install CakePHP with PEAR you’ll need to
do the following:
pear channel-discover pear.cakephp.org
pear install cakephp/CakePHP

Note: On some systems installing libraries with PEAR will require sudo.

32

Chapter 2. Installation

CakePHP Cookbook Documentation, Release 2.x

After installing CakePHP with PEAR, if PEAR is configured correctly you should be able to use the cake command tocreate a new application. Since CakePHP will be located on PHP’s include_path you won’t need to make any otherchanges.
Installing CakePHP with Composer
Before starting you should make sure that you have got an up to date PHP version:php -v
You should at least have got installed PHP 5.3.0 (CLI) or higher. Your webserver’s PHP version must also be of 5.3.0
or higher, and should best be the same version your command line interface (CLI) PHP version is of.
Installing Composer
Composer is a dependency management tool for PHP 5.3+. It solves many of the problems the PEAR installer has, andsimplifies managing multiple versions of libraries. Packagist15 is the main repository of Composer installable packages.
Since CakePHP also publishes releases to Packagist, you can install CakePHP using Composer16 .
• Installing Composer on Linux and Mac OS X
1. Run the installer script as described in the official Composer documentation17 and follow the instructions
to install Composer.
2. Execute the following command to move the composer.phar to a directory that is in your path:
mv composer.phar /usr/local/bin/composer• Installing Composer on Windows
For Windows systems, you can download Composer’s Windows installer here18 . Further instructions for Composer’s Windows installer can be found within the README here19 .
Create a CakePHP Project
Before installing CakePHP you’ll need to setup a composer.json file. A composer.json file for a CakePHP application
would look like the following:{"name": "example-app","require": {"cakephp/cakephp": "2.10.*"},"config": {"vendor-dir": "Vendor/"}}
15 https://packagist.org/

16 https://getcomposer.org

17 https://getcomposer.org/download/

18 https://github.com/composer/windows-setup/releases/
19 https://github.com/composer/windows-setup

Advanced Installation and URL Rewriting33

CakePHP Cookbook Documentation, Release 2.x

Save this JSON into composer.json in the APP directory of your project. Next download the composer.phar file into
your project. After you’ve downloaded Composer, install CakePHP. In the same directory as your composer.json
run the following:$ php composer.phar install
Once Composer has finished running you should have a directory structure that looks like:example-app/composer.phar
composer.json
Vendor/bin/
autoload.php
composer/cakephp/
You are now ready to generate the rest of your application skeleton:$ Vendor/bin/cake bake project <path to project>
By default bake will hard-code CAKE_CORE_INCLUDE_PATH. To make your application more portable you should
modify webroot/index.php, changing CAKE_CORE_INCLUDE_PATH to be a relative path:define('CAKE_CORE_INCLUDE_PATH',
ROOT . DS . APP_DIR . DS . 'Vendor' . DS . 'cakephp' . DS . 'cakephp' . DS . 'lib');Note: If you are planning to create unit tests for your application you’ll also need to make the above change towebroot/test.php
If you’re installing any other libraries with Composer, you’ll need to setup the autoloader, and work around an issue in
Composer’s autoloader. In your Config/bootstrap.php file add the following:// Load Composer autoload.
require APP . 'Vendor/autoload.php';// Remove and re-prepend CakePHP's autoloader as Composer thinks it is the// most important.
// See: http://goo.gl/kKVJO7
spl_autoload_unregister(array('App', 'load'));spl_autoload_register(array('App', 'load'), true, true);
You should now have a functioning CakePHP application installed via Composer. Be sure to keep the composer.json
and composer.lock file with the rest of your source code.

34

Chapter 2. Installation

CakePHP Cookbook Documentation, Release 2.x

Sharing CakePHP Libraries with multiple Applications
There may be some situations where you wish to place CakePHP’s directories on different places on the filesystem.
This may be due to a shared host restriction, or maybe you just want a few of your apps to share the same CakePHP
libraries. This section describes how to spread your CakePHP directories across a filesystem.
First, realize that there are three main parts to a CakePHP application:
1. The core CakePHP libraries, in /lib/Cake.
2. Your application code, in /app.
3. The application’s webroot, usually in /app/webroot.
Each of these directories can be located anywhere on your file system, with the exception of the webroot, which needs
to be accessible by your web server. You can even move the webroot folder out of the app folder as long as you tell
CakePHP where you’ve put it.
To configure your CakePHP installation, you’ll need to make some changes to the following files.
• /app/webroot/index.php• /app/webroot/test.php (if you use the Testing feature.)
There are three constants that you’ll need to edit: ROOT, APP_DIR, and CAKE_CORE_INCLUDE_PATH.
• ROOT should be set to the path of the directory that contains your app folder.
• APP_DIR should be set to the (base)name of your app folder.
• CAKE_CORE_INCLUDE_PATH should be set to the path of your CakePHP libraries folder.
Let’s run through an example so you can see what an advanced installation might look like in practice. Imagine that I
wanted to set up CakePHP to work as follows:• The CakePHP core libraries will be placed in /usr/lib/cake.
• My application’s webroot directory will be /var/www/mysite/.
• My application’s app directory will be /home/me/myapp.
Given this type of setup, I would need to edit my webroot/index.php file (which will end up at/var/www/mysite/index.php, in this example) to look like the following:// /app/webroot/index.php (partial, comments removed)if (!defined('ROOT')) {define('ROOT', DS . 'home' . DS . 'me');}if (!defined('APP_DIR')) {define ('APP_DIR', 'myapp');}
if (!defined('CAKE_CORE_INCLUDE_PATH')) {define('CAKE_CORE_INCLUDE_PATH', DS . 'usr' . DS . 'lib');}
It is recommended to use the DS constant rather than slashes to delimit file paths. This prevents any missing file errors
you might get as a result of using the wrong delimiter, and it makes your code more portable.

Advanced Installation and URL Rewriting35

CakePHP Cookbook Documentation, Release 2.x

Apache and mod_rewrite (and .htaccess)This section was moved to URL rewriting.

Fire It Up
Alright, let’s see CakePHP in action. Depending on which setup you used, you should point your browser to http://example.com/ or http://www.example.com/cake_2_0/. At this point, you’ll be presented with CakePHP’s default
home, and a message that tells you the status of your current database connection.
Congratulations! You are ready to create your first CakePHP application.
Not working? If you’re getting timezone related errors from PHP uncomment one line in app/Config/core.php:/*** Uncomment this line and correct your server timezone to fix* any date & time related errors.
*/
date_default_timezone_set('UTC');

36

Chapter 2. Installation

CHAPTER 3

CakePHP Overview

Welcome to the Cookbook, the manual for the CakePHP web application framework that makes developing a piece of
cake!
This manual assumes that you have a general understanding of PHP and a basic understanding of object-oriented
programming (OOP). Different functionality within the framework makes use of different technologies – such as SQL,JavaScript, and XML – and this manual does not attempt to explain those technologies, only how they are used incontext.

What is CakePHP? Why use it?
CakePHP20 is a free21 , open-source22 , rapid development23 framework24 for PHP25 . It’s a foundational structure forprogrammers to create web applications. Our primary goal is to enable you to work in a structured and rapid manner–without loss of flexibility.
CakePHP takes the monotony out of web development. It provides you with all the tools you need to get started coding
and what you need to get done: the logic specific to your application. Instead of reinventing the wheel every time you
begin a new project, check out a copy of CakePHP and get started with the logic of your application.
CakePHP has an active developer team26 and community, bringing great value to the project. In addition to keeping you
from wheel-reinventing, using CakePHP means your application’s core is well tested and is being constantly improved.
Here’s a quick list of features you’ll enjoy when using CakePHP:• Active, friendly Official CakePHP Forum
20 https://cakephp.org/

21 https://en.wikipedia.org/wiki/MIT_License
22 https://en.wikipedia.org/wiki/Open_source

23 https://en.wikipedia.org/wiki/Rapid_application_development
24 https://en.wikipedia.org/wiki/Application_framework
25 https://www.php.net/

26 https://github.com/cakephp?tab=members

37CakePHP Cookbook Documentation, Release 2.x• Flexible licensing27• Compatible with versions PHP 5.2.8 and greater• Integrated CRUD28 for database interaction• Application scaffolding29• Code generation• MVC30 architecture• Request dispatcher with clean, custom URLs and routes• Built-in validation31• Fast and flexible templating32 (PHP syntax, with helpers)• View helpers for AJAX, JavaScript, HTML forms and more
• Email, cookie, security, session, and request handling Components• Flexible ACL33• Data sanitization• Flexible caching34• Localization• Works from any web site directory, with little to no Apache35 configuration involved

Understanding Model-View-Controller
CakePHP follows the MVC36 software design pattern. Programming using MVC separates your application into threemain parts:

The Model layer
The Model layer represents the part of your application that implements the business logic. It is responsible for retrieving data and converting it into meaningful concepts for your application. This includes processing, validating,associating or other tasks related to handling data.
At a first glance, Model objects can be looked at as the first layer of interaction with any database you might be using
for your application. But in general they stand for the major concepts around which you implement your application.
In the case of a social network, the Model layer would take care of tasks such as saving the user data, saving friends’
associations, storing and retrieving user photos, finding suggestions for new friends, etc. The model objects can bethought as “Friend”, “User”, “Comment”, or “Photo”.
27 https://en.wikipedia.org/wiki/MIT_License

28 https://en.wikipedia.org/wiki/Create,_read,_update_and_delete
29 https://en.wikipedia.org/wiki/Scaffold_(programming)
30 https://en.wikipedia.org/wiki/Model-view-controller
31 https://en.wikipedia.org/wiki/Data_validation

32 https://en.wikipedia.org/wiki/Web_template_system
33 https://en.wikipedia.org/wiki/Access_control_list
34 https://en.wikipedia.org/wiki/Web_cache
35 https://httpd.apache.org/

36 https://en.wikipedia.org/wiki/Model-view-controller

38

Chapter 3. CakePHP Overview

CakePHP Cookbook Documentation, Release 2.x

The View layer
The View renders a presentation of modeled data. Being separated from the Model objects, it is responsible for using
the information it has available to produce any presentational interface your application might need.
For example, as the Model layer returns a set of data, the view would use it to render a HTML page containing it, or a
XML formatted result for others to consume.
The View layer is not only limited to HTML or text representation of the data. It can be used to deliver a wide variety
of formats depending on your needs, such as videos, music, documents and any other format you can think of.

The Controller layer
The Controller layer handles requests from users. It is responsible for rendering a response with the aid of both theModel and the View layer.
A controller can be seen as a manager that ensures that all resources needed for completing a task are delegated to thecorrect workers. It waits for petitions from clients, checks their validity according to authentication or authorization
rules, delegates data fetching or processing to the model, selects the type of presentational data that the clients are
accepting, and finally delegates the rendering process to the View layer.

CakePHP request cycle

Figure: 1: A typical MVC Request in CakePHPThe typical CakePHP request cycle starts with a user requesting a page or resource in your application. This request is
first processed by a dispatcher which will select the correct controller object to handle it.
Once the request arrives at the controller, it will communicate with the Model layer to process any data-fetching or-saving operation that might be needed. After this communication is over, the controller will proceed to delegate to thecorrect view object the task of generating output resulting from the data provided by the model.
Finally, when this output is generated, it is immediately rendered to the user.
Almost every request to your application will follow this basic pattern. We’ll add some details later on which are
specific to CakePHP, so keep this in mind as we proceed.

Understanding Model-View-Controller

39CakePHP Cookbook Documentation, Release 2.x

Benefits
Why use MVC? Because it is a tried and true software design pattern that turns an application into a maintainable,
modular, rapidly developed package. Crafting application tasks into separate models, views, and controllers makes
your application very light on its feet. New features are easily added, and new faces on old features are a snap. The
modular and separate design also allows developers and designers to work simultaneously, including the ability to
rapidly prototype37 . Separation also allows developers to make changes in one part of the application without affecting
the others.
If you’ve never built an application this way, it takes some time getting used to, but we’re confident that once you’ve
built your first application using CakePHP, you won’t want to do it any other way.
To get started on your first CakePHP application, try the blog tutorial now

Where to Get Help
The Official CakePHP website
https://cakephp.org
The Official CakePHP website is always a great place to visit. It features links to oft-used developer tools, screencasts,
donation opportunities, and downloads.

The Cookbook
https://book.cakephp.org
This manual should probably be the first place you go to get answers. As with many other open source projects, we
get new folks regularly. Try your best to answer your questions on your own first. Answers may come slower, but willremain longer – and you’ll also be lightening our support load. Both the manual and the API have an online component.

The Bakery
https://bakery.cakephp.org
The CakePHP Bakery is a clearing house for all things regarding CakePHP. Check it out for tutorials, case studies, andcode examples. Once you’re acquainted with CakePHP, log on and share your knowledge with the community and gain
instant fame and fortune.

The API
https://api.cakephp.org/2.x/
Straight to the point and straight from the core developers, the CakePHP API (Application Programming Interface) isthe most comprehensive documentation around for all the nitty gritty details of the internal workings of the framework.
It’s a straight forward code reference, so bring your propeller hat.
37 https://en.wikipedia.org/wiki/Software_prototyping

40

Chapter 3. CakePHP Overview

CakePHP Cookbook Documentation, Release 2.x

The Test Cases
If you ever feel the information provided in the API is not sufficient, check out the code of the test cases provided with
CakePHP. They can serve as practical examples for function and data member usage for a class.
lib/Cake/Test/Case

The IRC channel
IRC Channels on irc.freenode.net:• #cakephp – General Discussion• #cakephp-docs – Documentation• #cakephp-bakery – Bakery
If you’re stumped, give us a holler in the CakePHP IRC channel. Someone from the development team38 is usually
there, especially during the daylight hours for North and South America users. We’d love to hear from you, whether
you need some help, want to find users in your area, or would like to donate your brand new sports car.

Official CakePHP Forum
CakePHP Official Forum39
Our official forum is where you can ask for help, suggest ideas and have a talk about CakePHP. It’s a perfect place forquickly finding answers and help others. Join the CakePHP family by signing up.

Stackoverflow
https://stackoverflow.com/40
Tag your questions with cakephp and the specific version you are using to enable existing users of stackoverflow tofind your questions.

Where to get Help in your Language
Brazilian Portuguese• Brazilian CakePHP Community41
38 https://github.com/cakephp?tab=members
39 https://discourse.cakephp.org

40 https://stackoverflow.com/questions/tagged/cakephp/
41 https://cakephp-br.org

Where to Get Help41

CakePHP Cookbook Documentation, Release 2.x

Danish• Danish CakePHP Slack Channel42
French• French CakePHP Community43
German• German CakePHP Slack Channel44• German CakePHP Facebook Group45
Iranian• Iranian CakePHP Community46
Dutch• Dutch CakePHP Slack Channel47
Japanese• CakePHP JAPAN Facebook Group48
Portuguese• Portuguese CakePHP Google Group49
Spanish• Spanish CakePHP Slack Channel50• Spanish CakePHP IRC Channel• Spanish CakePHP Google Group51

42 https://cakesf.slack.com/messages/denmark/43 https://cakephp-fr.org

44 https://cakesf.slack.com/messages/german/

45 https://www.facebook.com/groups/146324018754907/
46 https://cakephp.ir

47 https://cakesf.slack.com/messages/netherlands/

48 https://www.facebook.com/groups/304490963004377/
49 https://groups.google.com/group/cakephp-pt
50 https://cakesf.slack.com/messages/spanish/

51 https://groups.google.com/group/cakephp-esp

42

Chapter 3. CakePHP Overview

CHAPTER 4

Controllers

Controllers are the ‘C’ in MVC. After routing has been applied and the correct controller has been found, your controller’s action is called. Your controller should handle interpreting the request data, making sure the correct models
are called, and the right response or view is rendered. Controllers can be thought of as middle man between the Model
and View. You want to keep your controllers thin, and your models fat. This will help you more easily reuse your codeand makes your code easier to test.
Commonly, a controller is used to manage the logic around a single model. For example, if you were building a site foran online bakery, you might have a RecipesController managing your recipes and an IngredientsController managing
your ingredients. However, it’s also possible to have controllers work with more than one model. In CakePHP, acontroller is named after the primary model it handles.
Your application’s controllers extend the AppController class, which in turn extends the core Controller class. The
AppController class can be defined in /app/Controller/AppController.php and it should contain methods thatare shared between all of your application’s controllers.
Controllers provide a number of methods that handle requests. These are called actions. By default, each public
method in a controller is an action, and is accessible from a URL. An action is responsible for interpreting the request
and creating the response. Usually responses are in the form of a rendered view, but there are other ways to create
responses as well.

The App Controller
As stated in the introduction, the AppController class is the parent class to all of your application’s controllers.
AppController itself extends the Controller class included in the CakePHP core library. AppController is defined in /app/Controller/AppController.php as follows:class AppController extends Controller {}43

CakePHP Cookbook Documentation, Release 2.x

Controller attributes and methods created in your AppController will be available to all of your application’s controllers. Components (which you’ll learn about later) are best used for code that is used in many (but not necessarilyall) controllers.
While normal object-oriented inheritance rules apply, CakePHP does a bit of extra work when it comes to special controller attributes. The components and helpers used by a controller are treated specially. In these cases,
AppController value arrays are merged with child controller class arrays. The values in the child class will always
override those in AppController.
Note: CakePHP merges the following variables from the AppController into your application’s controllers:• $components• $helpers• $uses
Remember to add the default Html and Form helpers if you define the $helpers property in your AppController.
Also remember to call AppController’s callbacks within child controller callbacks for best results:
public function beforeFilter() {parent::beforeFilter();}Request parameters
When a request is made to a CakePHP application, CakePHP’s Router and Dispatcher classes use Routes Configuration to find and create the correct controller. The request data is encapsulated in a request object. CakePHP puts
all of the important request information into the $this->request property. See the section on CakeRequest for more
information on the CakePHP request object.

Controller actions
Controller actions are responsible for converting the request parameters into a response for the browser/user making therequest. CakePHP uses conventions to automate this process and remove some boilerplate code you would otherwise
need to write.
By convention, CakePHP renders a view with an inflected version of the action name. Returning to our online bakery
example, our RecipesController might contain the view(), share(), and search() actions. The controller would befound in /app/Controller/RecipesController.php and contain:# /app/Controller/RecipesController.php
class RecipesController extends AppController {public function view($id) {//action logic goes here..
}public function share($customerId, $recipeId) {//action logic goes here..
}(continues on next page)

44

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

public function search($query) {//action logic goes here..
}}
The view files for these actions would be app/View/Recipes/view.ctp, app/View/Recipes/share.ctp, andapp/View/Recipes/search.ctp. The conventional view file name is the lowercased and underscored version of theaction name.
Controller actions generally use set() to create a context that View uses to render the view. Because of the conventions
that CakePHP uses, you don’t need to create and render the view manually. Instead, once a controller action has
completed, CakePHP will handle rendering and delivering the View.
If for some reason you’d like to skip the default behavior, both of the following techniques will bypass the default view
rendering behavior.
• If you return a string, or an object that can be converted to a string from your controller action, it will be used as
the response body.
• You can return a CakeResponse object with the completely created response.
When you use controller methods with requestAction(), you will often want to return data that isn’t a string. If
you have controller methods that are used for normal web requests + requestAction, you should check the request type
before returning:class RecipesController extends AppController {public function popular() {$popular = $this->Recipe->popular();if (!empty($this->request->params['requested'])) {return $popular;}$this->set('popular', $popular);}}
The above controller action is an example of how a method can be used with requestAction() and normal requests. Returning array data to a non-requestAction request will cause errors and should be avoided. See the section
on requestAction() for more tips on using requestAction()
In order for you to use a controller effectively in your own application, we’ll cover some of the core attributes andmethods provided by CakePHP’s controllers.

Request Life-cycle callbacks
class Controller
CakePHP controllers come fitted with callbacks you can use to insert logic around the request life-cycle:
Controller::beforeFilter()This function is executed before every action in the controller. It’s a handy place to check for an active session
or inspect user permissions.

Request Life-cycle callbacks

45

CakePHP Cookbook Documentation, Release 2.x

Note: The beforeFilter() method will be called for missing actions, and scaffolded actions.
Controller::beforeRender()Called after controller action logic, but before the view is rendered. This callback is not used often, but may beneeded if you are calling render() manually before the end of a given action.
Controller::afterFilter()Called after every controller action, and after rendering is complete. This is the last controller method to run.
In addition to controller life-cycle callbacks, Components also provide a similar set of callbacks.

Controller Methods
For a complete list of controller methods and their descriptions visit the CakePHP API52 .

Interacting with Views
Controllers interact with views in a number of ways. First, they are able to pass data to the views, using set(). You
can also decide which view class to use, and which view file should be rendered from the controller.
Controller::set(string $var, mixed $value)
The set() method is the main way to send data from your controller to your view. Once you’ve used set(), thevariable can be accessed in your view:// First you pass data from the controller:$this->set('color', 'pink');// Then, in the view, you can utilize the data:?>
You have selected <?php echo $color; ?> icing for the cake.
The set() method also takes an associative array as its first parameter. This can often be a quick way to assign
a set of information to the view:$data = array('color' => 'pink','type' => 'sugar','base_price' => 23.95);// make $color, $type, and $base_price// available to the view:$this->set($data);
The attribute $pageTitle no longer exists. Use set() to set the title:
52 https://api.cakephp.org/2.x/class-Controller.html

46

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x$this->set('title_for_layout', 'This is the page title');As of 2.5 the variable $title_for_layout is deprecated, use view blocks instead.
Controller::render(string $view, string $layout)
The render() method is automatically called at the end of each requested controller action. This method performs all the view logic (using the data you’ve submitted using the set() method), places the view inside its$layout, and serves it back to the end user.
The default view file used by render is determined by convention. If the search() action of the RecipesController
is requested, the view file in /app/View/Recipes/search.ctp will be rendered:class RecipesController extends AppController {// ...
public function search() {// Render the view in /View/Recipes/search.ctp$this->render();}// ...
}
Although CakePHP will automatically call it after every action’s logic (unless you’ve set $this->autoRenderto false), you can use it to specify an alternate view file by specifying a view name in the controller using $view.
If $view starts with ‘/’, it is assumed to be a view or element file relative to the /app/View folder. This allows
direct rendering of elements, very useful in AJAX calls.
// Render the element in /View/Elements/ajaxreturn.ctp$this->render('/Elements/ajaxreturn');
The $layout parameter allows you to specify the layout with which the view is rendered.
Rendering a specific view
In your controller, you may want to render a different view than the conventional one. You can do this by calling
render() directly. Once you have called render(), CakePHP will not try to re-render the view:class PostsController extends AppController {public function my_action() {$this->render('custom_file');}}This would render app/View/Posts/custom_file.ctp instead of app/View/Posts/my_action.ctp
You can also render views inside plugins using the following syntax:
PluginController/custom_file'). For example:$this->render('PluginName.

class PostsController extends AppController {public function my_action() {$this->render('Users.UserDetails/custom_file');}}This would render app/Plugin/Users/View/UserDetails/custom_file.ctp

Controller Methods

47

CakePHP Cookbook Documentation, Release 2.x

Flow Control
Controller::redirect(mixed $url, integer $status, boolean $exit)
The flow control method you’ll use most often is redirect(). This method takes its first parameter in the formof a CakePHP-relative URL. When a user has successfully placed an order, you might wish to redirect them to areceipt screen.
public function place_order() {// Logic for finalizing order goes here
if ($success) {return $this->redirect(
array('controller' => 'orders', 'action' => 'thanks'));}
return $this->redirect(
array('controller' => 'orders', 'action' => 'confirm'));}
You can also use a relative or absolute URL as the $url argument:$this->redirect('/orders/thanks');$this->redirect('http://www.example.com');
You can also pass data to the action:$this->redirect(array('action' => 'edit', $id));
The second parameter of redirect() allows you to define an HTTP status code to accompany the redirect. You
may want to use 301 (moved permanently) or 303 (see other), depending on the nature of the redirect.
The method will issue an exit() after the redirect unless you set the third parameter to false.
If you need to redirect to the referer page you can use:
$this->redirect($this->referer());
The method also supports name-based parameters. If you want to redirect to a URL like: http://www.
example.com/orders/confirm/product:pizza/quantity:5 you can use:
$this->redirect(array('controller' => 'orders','action' => 'confirm','product' => 'pizza','quantity' => 5));
An example using query strings and hash would look like:$this->redirect(array('controller' => 'orders','action' => 'confirm','?' => array('product' => 'pizza','quantity' => 5
(continues on next page)48

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)),'#' => 'top'));
The generated URL would be:
http://www.example.com/orders/confirm?product=pizza&quantity=5#top
Controller::flash(string $message, string|array $url, integer $pause, string $layout)
Like redirect(), the flash() method is used to direct a user to a new page after an operation. The flash()method is different in that it shows a message before passing the user on to another URL.
The first parameter should hold the message to be displayed, and the second parameter is a CakePHP-relative
URL. CakePHP will display the $message for $pause seconds before forwarding the user on.
If there’s a particular template you’d like your flashed message to use, you may specify the name of that layout
in the $layout parameter.
For in-page flash messages, be sure to check out SessionComponent::setFlash() method.

Callbacks
In addition to the Request Life-cycle callbacks, CakePHP also supports callbacks related to scaffolding.
Controller::beforeScaffold($method)$method name of method called example index, edit, etc.
Controller::afterScaffoldSave($method)$method name of method called either edit or update.
Controller::afterScaffoldSaveError($method)$method name of method called either edit or update.
Controller::scaffoldError($method)$method name of method called example index, edit, etc.

Other Useful Methods
Controller::constructClasses()This method loads the models required by the controller. This loading process is done by CakePHP normally,
but this method is handy to have when accessing controllers from a different perspective. If you need CakePHP
in a command-line script or some other outside use, constructClasses() may come in handy.
Controller::referer(mixed $default = null, boolean $local = false)
Returns the referring URL for the current request. Parameter $default can be used to supply a default URL to
use if HTTP_REFERER cannot be read from headers. So, instead of doing this:class UserController extends AppController {public function delete($id) {// delete code goes here, and then...
if ($this->referer() != '/') {return $this->redirect($this->referer());}(continues on next page)

Controller Methods

49CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

return $this->redirect(array('action' => 'index'));}}you can do this:class UserController extends AppController {public function delete($id) {// delete code goes here, and then...
return $this->redirect($this->referer(array('action' => 'index')));}}If $default is not set, the function defaults to the root of your domain - ‘/’.
Parameter $local if set to true, restricts referring URLs to local server.
Controller::disableCache()
Used to tell the user’s browser not to cache the results of the current request. This is different than view caching,covered in a later chapter.
The headers sent to this effect are:Expires: Mon, 26 Jul 1997 05:00:00 GMT
Last-Modified: [current datetime] GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0
Pragma: no-cacheController::postConditions(array $data, mixed $op, string $bool, boolean $exclusive)
Use this method to turn a set of POSTed model data (from HtmlHelper-compatible inputs) into a set of findconditions for a model. This function offers a quick shortcut on building search logic. For example, an administrative user may want to be able to search orders in order to know which items need to be shipped. You can use
CakePHP’s FormHelper and HtmlHelper to create a quick form based on the Order model. Then a controller
action can use the data posted from that form to craft find conditions:public function index() {$conditions = $this->postConditions($this->request->data);$orders = $this->Order->find('all', compact('conditions'));$this->set('orders', $orders);}
If $this->request->data['Order']['destination'] equals “Old Towne Bakery”, postConditions converts that condition to an array compatible for use in a Model->find() method. In this case, array('Order.
destination' => 'Old Towne Bakery').
If you want to use a different SQL operator between terms, supply them using the second parameter:/*
Contents of $this->request->dataarray('Order' => array((continues on next page)

50

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'num_items' => '4',
'referrer' => 'Ye Olde'))*/// Let's get orders that have at least 4 items and contain 'Ye Olde'$conditions = $this->postConditions($this->request->data,array('num_items' => '>=','referrer' => 'LIKE'));$orders = $this->Order->find('all', compact('conditions'));
The third parameter allows you to tell CakePHP what SQL boolean operator to use between the find conditions.
Strings like ‘AND’, ‘OR’ and ‘XOR’ are all valid values.
Finally, if the last parameter is set to true, and the $op parameter is an array, fields not included in $op will not
be included in the returned conditions.
Controller::paginate()
This method is used for paginating results fetched by your models. You can specify page sizes, model findconditions and more. See the pagination section for more details on how to use paginate.
Controller::requestAction(string $url, array $options)This function calls a controller’s action from any location and returns data from the action. The $url passed isa CakePHP-relative URL (/controllername/actionname/params). To pass extra data to the receiving controller
action add to the $options array.
Note: You can use requestAction() to retrieve a fully rendered view by passing ‘return’ in the options:requestAction($url, array('return'));. It is important to note that making a requestAction() using
return from a controller method can cause script and CSS tags to not work correctly.

Warning: If used without caching requestAction() can lead to poor performance. It is rarely appropriate
to use in a controller or model.
requestAction() is best used in conjunction with (cached) elements – as a way to fetch data for an element
before rendering. Let’s use the example of putting a “latest comments” element in the layout. First we need tocreate a controller function that will return the data:// Controller/CommentsController.php
class CommentsController extends AppController {public function latest() {if (empty($this->request->params['requested'])) {throw new ForbiddenException();}return $this->Comment->find('all',
array('order' => 'Comment.created DESC', 'limit' => 10)

Controller Methods(continues on next page)51

CakePHP Cookbook Documentation, Release 2.x(continued from previous page));}}You should always include checks to make sure your requestAction() methods are actually originating fromrequestAction(). Failing to do so will allow requestAction() methods to be directly accessible from a
URL, which is generally undesirable.
If we now create a simple element to call that function:// View/Elements/latest_comments.ctp$comments = $this->requestAction('/comments/latest');foreach ($comments as $comment) {echo $comment['Comment']['title'];}We can then place that element anywhere to get the output using:echo $this->element('latest_comments');
Written in this way, whenever the element is rendered, a request will be made to the controller to get the data,
the data will be processed, and returned. However in accordance with the warning above it’s best to make use ofelement caching to prevent needless processing. By modifying the call to element to look like this:echo $this->element('latest_comments', array(), array('cache' => true));
The requestAction() call will not be made while the cached element view file exists and is valid.
In addition, requestAction() now takes array based cake style URLs:
echo $this->requestAction(array('controller' => 'articles', 'action' => 'featured'),array('return'));
This allows the requestAction() call to bypass the usage of Router::url() which can increase performance. The url based arrays are the same as the ones that HtmlHelper::link() uses with one difference if you are using named or passed parameters, you must put them in a second array and wrap them with thecorrect key. This is because requestAction() merges the named args array (requestAction’s 2nd parameter) with the Controller::params member array and does not explicitly place the named args array into thekey ‘named’; Additional members in the $option array will also be made available in the requested action’sController::params array:echo $this->requestAction('/articles/featured/limit:3');
echo $this->requestAction('/articles/view/5');
As an array in the requestAction() would then be:echo $this->requestAction(array('controller' => 'articles', 'action' => 'featured'),array('named' => array('limit' => 3)));(continues on next page)

52

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)echo $this->requestAction(array('controller' => 'articles', 'action' => 'view'),array('pass' => array(5)));

Note: Unlike other places where array URLs are analogous to string URLs, requestAction() treats themdifferently.
When using an array url in conjunction with requestAction() you must specify all parameters that you willneed in the requested action. This includes parameters like $this->request->data. In addition to passing all
required parameters, named and pass parameters must be done in the second array as seen above.
Controller::loadModel(string $modelClass, mixed $id)
The loadModel() function comes handy when you need to use a model which is not the controller’s default
model or its associated model:$this->loadModel('Article');$recentArticles = $this->Article->find('all',
array('limit' => 5, 'order' => 'Article.created DESC'));$this->loadModel('User', 2);$user = $this->User->read();Controller Attributes
For a complete list of controller attributes and their descriptions visit the CakePHP API53 .
property Controller::$name
The $name attribute should be set to the name of the controller. Usually this is just the plural form of the primarymodel the controller uses. This property can be omitted, but saves CakePHP from inflecting it:// $name controller attribute usage example
class RecipesController extends AppController {public $name = 'Recipes';}
53 https://api.cakephp.org/2.x/class-Controller.html

Controller Attributes

53

CakePHP Cookbook Documentation, Release 2.x$components, $helpers and $uses
The next most often used controller attributes tell CakePHP what $helpers, $components, and models you’ll beusing in conjunction with the current controller. Using these attributes make MVC classes given by $components and$uses available to the controller as class variables ($this->ModelName, for example) and those given by $helpers
to the view as an object reference variable ($this->{$helpername}).
Note: Each controller has some of these classes available by default, so you may not need to configure your controller
at all.
property Controller::$uses
Controllers have access to their primary model available by default. Our RecipesController will have theRecipe model class available at $this->Recipe, and our ProductsController also features the Product model at$this->Product. However, when allowing a controller to access additional models through the $uses variable,
the name of the current controller’s model must also be included. This is illustrated in the example below.
If you do not wish to use a Model in your controller, set public $uses = array(). This will allow you to use
a controller without a need for a corresponding Model file. However, the models defined in the AppController
will still be loaded. You can also use false to not load any models at all. Even those defined in the
AppController.
Changed in version 2.1: $uses now has a new default value, it also handles false differently.
property Controller::$helpers
The HtmlHelper, FormHelper, and SessionHelper are available by default, as is the SessionComponent.
But if you choose to define your own $helpers array in AppController, make sure to include HtmlHelperand FormHelper if you want them still available by default in your Controllers. To learn more about theseclasses, be sure to check out their respective sections later in this manual.
Let’s look at how to tell a CakePHP Controller that you plan to use additional MVC classes:
class RecipesController extends AppController {public $uses = array('Recipe', 'User');
public $helpers = array('Js');
public $components = array('RequestHandler');}
Each of these variables are merged with their inherited values, therefore it is not necessary (for example) to
redeclare the FormHelper, or anything that is declared in your AppController.
property Controller::$components
The components array allows you to set which Components a controller will use. Like $helpers and $uses
components in your controllers are merged with those in AppController. As with $helpers you can passsettings into $components. See Configuring Components for more information.

54

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x

Other Attributes
While you can check out the details for all controller attributes in the API54 , there are other controller attributes thatmerit their own sections in the manual.
property Controller::$cacheAction
The cacheAction attribute is used to define the duration and other information about full page caching. You canread more about full page caching in the CacheHelper documentation.
property Controller::$paginate
The paginate attribute is a deprecated compatibility property.
Using it loads and configures the
PaginatorComponent. It is recommended that you update your code to use normal component settings:
class ArticlesController extends AppController {public $components = array('Paginator' => array('Article' => array('conditions' => array('published' => 1)
)
)
);
}

More on controllers
Request and Response objects
New in CakePHP 2.0 are request and response objects. In previous versions, these objects were represented
through arrays, and the related methods were spread across RequestHandlerComponent, Router, Dispatcher andController. There was no authoritative object on what information the request contained. For 2.0, CakeRequest
and CakeResponse are used for this purpose.

CakeRequest
CakeRequest is the default request object used in CakePHP. It centralizes a number of features for interrogating
and interacting with request data. On each request, one CakeRequest is created and then passed by reference to thevarious layers of an application that use request data. By default, CakeRequest is assigned to $this->request, andis available in Controllers, Views and Helpers. You can also access it in Components by using the controller reference.
Some of the duties CakeRequest performs include:• Process the GET, POST, and FILES arrays into the data structures you are familiar with.
• Provide environment introspection pertaining to the request. Things like the headers sent, the client’s IP address,
and the subdomain/domain information about the application the server is running on.
• Provide access to request parameters both as array indexes and object properties.
54 https://api.cakephp.org

More on controllers

55CakePHP Cookbook Documentation, Release 2.x

Accessing request parameters
CakeRequest exposes several interfaces for accessing request parameters. The first uses object properties, the second
uses array indexes, and the third uses $this->request->params:$this->request->controller;$this->request['controller'];$this->request->params['controller'];
All of the above will access the same value. Multiple ways of accessing the parameters have been provided to easemigration for existing applications. All Route Elements are accessed through this interface.
In addition to Route Elements, you also often need access to Passed Arguments and Named Parameters. These are bothavailable on the request object as well:// Passed arguments$this->request->pass;$this->request['pass'];$this->request->params['pass'];// named parameters$this->request->named;$this->request['named'];$this->request->params['named'];All of these will provide you access to the passed arguments and named parameters. There are several important/usefulparameters that CakePHP uses internally. These are also all found in the request parameters:• plugin The plugin handling the request. Will be null when there is no plugin.
• controller The controller handling the current request.
• action The action handling the current request.
• prefix The prefix for the current action. See Prefix Routing for more information.
• bare Present when the request came from requestAction() and included the bare option. Bare requests do
not have layouts rendered.
• requested Present and set to true when the action came from requestAction().
Accessing Querystring parameters
Querystring parameters can be read using CakeRequest::$query:// URL is /posts/index?page=1&sort=title$this->request->query['page'];// You can also access it via an array// Note: BC accessor, will be deprecated in future versions$this->request['url']['page'];
You can either directly access the $query property, or you can use CakeRequest::query() to read the URL query
array in an error-free manner. Any keys that do not exist will return null:$foo = $this->request->query('value_that_does_not_exist');// $foo === null

56

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x

Accessing POST data
All POST data can be accessed using CakeRequest::$data. Any form data that contains a data prefix will have thatdata prefix removed. For example:// An input with a name attribute equal to 'data[MyModel][title]'// is accessible at$this->request->data['MyModel']['title'];You can either directly access the $data property, or you can use CakeRequest::data() to read the data array in anerror-free manner. Any keys that do not exist will return null:$foo = $this->request->data('Value.that.does.not.exist');// $foo == null

Accessing PUT or POST data
New in version 2.2.
When building REST services, you often accept request data on PUT and DELETE requests. As of 2.2, any
application/x-www-form-urlencoded request body data will automatically be parsed and set to $this->data
for PUT and DELETE requests. If you are accepting JSON or XML data, see below for how you can access those request
bodies.
Accessing XML or JSON data
Applications employing REST often exchange data in non-URL-encoded post bodies. You can read input data in any
format using CakeRequest::input(). By providing a decoding function, you can receive the content in a deserialized
format:// Get JSON encoded data submitted to a PUT/POST action$data = $this->request->input('json_decode');Some deserializing methods require additional parameters when called, such as the ‘as array’ parameter on
json_decode. If you want XML converted into a DOMDocument object, CakeRequest::input() supports passing
in additional parameters as well:// Get Xml encoded data submitted to a PUT/POST action$data = $this->request->input('Xml::build', array('return' => 'domdocument'));Accessing path information
CakeRequest also provides useful information about the paths in your application. CakeRequest::$base and
CakeRequest::$webroot are useful for generating URLs, and determining whether or not your application is in
a subdirectory.

More on controllers

57

CakePHP Cookbook Documentation, Release 2.x

Inspecting the request
Detecting various request conditions used to require using RequestHandlerComponent. These methods have been
moved to CakeRequest, and offer a new interface alongside a more backwards-compatible usage:$this->request->is('post');$this->request->isPost(); // deprecatedBoth method calls will return the same value.
For the time being, the methods are still available on
RequestHandlerComponent, but are deprecated and will be removed in 3.0.0. You can also easily extend the request detectors that are available by using CakeRequest::addDetector() to create new kinds of detectors. There
are four different types of detectors that you can create:• Environment value comparison - Compares a value fetched from env() for equality with the provided value.
• Pattern value comparison - Pattern value comparison allows you to compare a value fetched from env() to aregular expression.
• Option based comparison - Option based comparisons use a list of options to create a regular expression. Subsequent calls to add an already defined options detector will merge the options.
• Callback detectors - Callback detectors allow you to provide a ‘callback’ type to handle the check. The callback
will receive the request object as its only parameter.
Some examples would be:// Add an environment detector.
$this->request->addDetector('post',
array('env' => 'REQUEST_METHOD', 'value' => 'POST'));// Add a pattern value detector.
$this->request->addDetector('iphone',array('env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i'));// Add an option detector.
$this->request->addDetector('internalIp', array('env' => 'CLIENT_IP','options' => array('192.168.0.101', '192.168.0.100')));// Add a callback detector. Can either be an anonymous function// or a regular callable.
$this->request->addDetector('awesome',array('callback' => function ($request) {return isset($request->awesome);}));
CakeRequest also includes methods like CakeRequest::domain(), CakeRequest::subdomains() and
CakeRequest::host() to help applications with subdomains.
There are several built-in detectors that you can use:

58

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x• is('get') Check to see whether the current request is a GET.
• is('put') Check to see whether the current request is a PUT.
• is('post') Check to see whether the current request is a POST.
• is('delete') Check to see whether the current request is a DELETE.
• is('head') Check to see whether the current request is HEAD.
• is('options') Check to see whether the current request is OPTIONS.
• is('ajax') Check to see whether the current request came with X-Requested-With = XMLHttpRequest.
• is('ssl') Check to see whether the request is via SSL• is('flash') Check to see whether the request has a User-Agent of Flash• is('mobile') Check to see whether the request came from a common list of mobile agents.
CakeRequest and RequestHandlerComponent
Since many of the features CakeRequest offers used to be the realm of RequestHandlerComponent, some rethinking
was required to figure out how it still fits into the picture. For 2.0, RequestHandlerComponent provides a layer ofsugar, such as switching layout and views based on content, on top of the utility that CakeRequest affords. This
separation of utility and sugar between the two classes lets you more easily choose what you want.
Interacting with other aspects of the request
You can use CakeRequest to introspect a variety of things about the request. Beyond the detectors, you can also findout other information from various properties and methods.
• $this->request->webroot contains the webroot directory.
• $this->request->base contains the base path.
• $this->request->here contains the full address to the current request.
• $this->request->query contains the query string parameters.
CakeRequest APIclass CakeRequest
CakeRequest encapsulates request parameter handling and introspection.
CakeRequest::domain($tldLength = 1)
Returns the domain name your application is running on.
CakeRequest::subdomains($tldLength = 1)
Returns the subdomains your application is running on as an array.
CakeRequest::host()
Returns the host your application is on.
CakeRequest::method()
Returns the HTTP method the request was made with.

More on controllers

59CakePHP Cookbook Documentation, Release 2.x

CakeRequest::onlyAllow($methods)
Set allowed HTTP methods. If not matched, will throw MethodNotAllowedException. The 405 response will
include the required Allow header with the passed methods
New in version 2.3.
Deprecated since version 2.5: Use CakeRequest::allowMethod() instead.
CakeRequest::allowMethod($methods)
Set allowed HTTP methods. If not matched will throw MethodNotAllowedException. The 405 response will
include the required Allow header with the passed methods
New in version 2.5.
CakeRequest::referer($local = false)
Returns the referring address for the request.
CakeRequest::clientIp($safe = true)
Returns the current visitor’s IP address.
CakeRequest::header($name)Allows you to access any of the HTTP_* headers that were used for the request. For example:$this->request->header('User-Agent');
would return the user agent used for the request.
CakeRequest::input($callback [, $options ])Retrieve the input data for a request, and optionally pass it through a decoding function. Useful when interactingwith XML or JSON request body content. Additional parameters for the decoding function can be passed asarguments to input():$this->request->input('json_decode');CakeRequest::data($name)
Provides dot notation access to request data. Allows request data to be read and modified. Calls can be chained
together as well:// Modify some request data, so you can prepopulate some form fields.
$this->request->data('Post.title', 'New post')->data('Comment.1.author', 'Mark');// You can also read out data.
$value = $this->request->data('Post.title');CakeRequest::query($name)
Provides dot notation access to URL query data:// URL is /posts/index?page=1&sort=title$value = $this->request->query('page');
New in version 2.3.
CakeRequest::is($type)
Check whether or not a Request matches a certain criterion. Uses the built-in detection rules as well as any
additional rules defined with CakeRequest::addDetector().

60

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x

CakeRequest::addDetector($name, $options)
Add a detector to be used with CakeRequest::is(). See Inspecting the request for more information.
CakeRequest::accepts($type = null)
Find out which content types the client accepts, or check whether it accepts a particular type of content.
Get all types:$this->request->accepts();
Check for a single type:$this->request->accepts('application/json');static CakeRequest::acceptLanguage($language = null)Get all the languages accepted by the client, or check whether a specific language is accepted.
Get the list of accepted languages:
CakeRequest::acceptLanguage();
Check whether a specific language is accepted:
CakeRequest::acceptLanguage('es-es');
CakeRequest::param($name)
Safely read values in $request->params. This removes the need to call isset() or empty() before usingparam values.
New in version 2.4.
property CakeRequest::$data
An array of POST data. You can use CakeRequest::data() to read this property in a way that suppresses
notice errors.
property CakeRequest::$query
An array of query string parameters.
property CakeRequest::$params
An array of route elements and request parameters.
property CakeRequest::$here
Returns the current request uri.
property CakeRequest::$base
The base path to the application, usually / unless your application is in a subdirectory.
property CakeRequest::$webrootThe current webroot.

More on controllers

61CakePHP Cookbook Documentation, Release 2.x

CakeResponse
CakeResponse is the default response class in CakePHP. It encapsulates a number of features and functionality for
generating HTTP responses in your application. It also assists in testing, as it can be mocked/stubbed allowing you toinspect headers that will be sent. Like CakeRequest, CakeResponse consolidates a number of methods previously
found on Controller, RequestHandlerComponent and Dispatcher. The old methods are deprecated in favour ofusing CakeResponse.
CakeResponse provides an interface to wrap the common response-related tasks such as:• Sending headers for redirects.
• Sending content type headers.
• Sending any header.
• Sending the response body.
Changing the response class
CakePHP uses CakeResponse by default. CakeResponse is a flexible and transparent class. If you need to override
it with your own application-specific class, you can replace CakeResponse in app/webroot/index.php. This will
make all the controllers in your application use CustomResponse instead of CakeResponse. You can also replace theresponse instance by setting $this->response in your controllers. Overriding the response object is handy during
testing, as it allows you to stub out the methods that interact with header(). See the section on CakeResponse andtesting for more information.
Dealing with content types
You can control the Content-Type of your application’s responses with CakeResponse::type(). If your application needs to deal with content types that are not built into CakeResponse, you can map them with
CakeResponse::type() as well:// Add a vCard type$this->response->type(array('vcf' => 'text/v-card'));// Set the response Content-Type to vcard.
$this->response->type('vcf');
Usually, you’ll want to map additional content types in your controller’s beforeFilter() callback, so you can leverage
the automatic view switching features of RequestHandlerComponent if you are using it.
Sending files
There are times when you want to send files as responses for your requests. Prior to version 2.3, you could use
MediaView. As of 2.3, MediaView is deprecated and you can use CakeResponse::file() to send a file as response:
public function sendFile($id) {$file = $this->Attachment->getFile($id);$this->response->file($file['path']);// Return response object to prevent controller from trying to render// a view
return $this->response;}

62

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x

As shown in the above example, you must pass the file path to the method. CakePHP will send a proper content typeheader if it’s a known file type listed in CakeResponse::$_mimeTypes. You can add new types prior to calling
CakeResponse::file() by using the CakeResponse::type() method.
If you want, you can also force a file to be downloaded instead of displayed in the browser by specifying the options:$this->response->file($file['path'],array('download' => true, 'name' => 'foo'));

Sending a string as file
You can respond with a file that does not exist on the disk, such as a pdf or an ics generated on the fly from a string:
public function sendIcs() {$icsString = $this->Calendar->generateIcs();$this->response->body($icsString);$this->response->type('ics');//Optionally force file download$this->response->download('filename_for_download.ics');// Return response object to prevent controller from trying to render// a view
return $this->response;}

Setting headers
Setting headers is done with the CakeResponse::header() method. It can be called with a few different parameter
configurations:// Set a single header$this->response->header('Location', 'http://example.com');// Set multiple headers$this->response->header(array('Location' => 'http://example.com','X-Extra' => 'My header'));$this->response->header(array('WWW-Authenticate: Negotiate','Content-type: application/pdf'));
Setting the same header() multiple times will result in overwriting the previous values, just as regular header calls
do. Headers are not sent when CakeResponse::header() is called; instead they are buffered until the response isactually sent.
New in version 2.4.

More on controllers

63CakePHP Cookbook Documentation, Release 2.x

You can now use the convenience method CakeResponse::location() to directly set or get the redirect location
header.
Interacting with browser caching
You sometimes need to force browsers not to
CakeResponse::disableCache() is intended for just that:

cache

the

resultsofacontroller

action.

public function index() {// do something.
$this->response->disableCache();}Warning: Using disableCache() with downloads from SSL domains while trying to send files to Internet Explorer
can result in errors.
You can also tell clients that you want them to cache responses. By using CakeResponse::cache():public function index() {//do something$this->response->cache('-1 minute', '+5 days');}
The above would tell clients to cache the resulting response for 5 days, hopefully speeding up your visitors’ experience.
CakeResponse::cache() sets the Last-Modified value to the first argument. Expires header and the max-age
directive are set based on the second parameter. Cache-Control’s public directive is set as well.
Fine tuning HTTP cache
One of the best and easiest ways of speeding up your application is to use HTTP cache. Under this caching model, you
are only required to help clients decide if they should use a cached copy of the response by setting a few headers suchas modified time and response entity tag.
Rather than forcing you to code the logic for caching and for invalidating (refreshing) it once the data has changed,
HTTP uses two models, expiration and validation, which usually are much simpler to use.
Apart from using CakeResponse::cache(), you can also use many other methods to fine-tune HTTP cache headers
to take advantage of browser or reverse proxy caching.
The Cache Control header
New in version 2.1.
Used under the expiration model, this header contains multiple indicators that can change the way browsers or proxies
use the cached content. A Cache-Control header can look like this:Cache-Control: private, max-age=3600, must-revalidate
CakeResponse class helps you set this header with some utility methods that will produce a final valid Cache-Control
header. The first is the CakeResponse::sharable() method, which indicates whether a response is to be consideredsharable across different users or clients. This method actually controls the public or private part of this header.

64

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x

Setting a response as private indicates that all or part of it is intended for a single user. To take advantage of sharedcaches, the control directive must be set as public.
The second parameter of this method is used to specify a max-age for the cache, which is the number of seconds after
which the response is no longer considered fresh:
public function view() {...
// set the Cache-Control as public for 3600 seconds$this->response->sharable(true, 3600);}
public function my_data() {...
// set the Cache-Control as private for 3600 seconds$this->response->sharable(false, 3600);}
CakeResponse exposes separate methods for setting each of the directives in the Cache-Control header.
The Expiration header
New in version 2.1.
You can set the Expires header to a date and time after which the response is no longer considered fresh. This header
can be set using the CakeResponse::expires() method:
public function view() {$this->response->expires('+5 days');}This method also accepts a DateTime instance or any string that can be parsed by the DateTime class.
The Etag header
New in version 2.1.
Cache validation in HTTP is often used when content is constantly changing, and asks the application to only generate
the response contents if the cache is no longer fresh. Under this model, the client continues to store pages in the cache,but it asks the application every time whether the resource has changed, instead of using it directly. This is commonlyused with static resources such as images and other assets.
The etag() method (called entity tag) is a string that uniquely identifies the requested resource, as a checksum does
for a file, in order to determine whether it matches a cached resource.
To take advantage of this header, you must either call the CakeResponse::checkNotModified() method manuallyor include the RequestHandlerComponent in your controller:
public function index() {$articles = $this->Article->find('all');$this->response->etag($this->Article->generateHash($articles));if ($this->response->checkNotModified($this->request)) {return $this->response;}(continues on next page)

More on controllers

65CakePHP Cookbook Documentation, Release 2.x(continued from previous page)...
}

The Last Modified header
New in version 2.1.
Under the HTTP cache validation model, you can also set the Last-Modified header to indicate the date and time atwhich the resource was modified for the last time. Setting this header helps CakePHP tell caching clients whether the
response was modified or not based on their cache.
To take advantage of this header, you must either call the CakeResponse::checkNotModified() method manuallyor include the RequestHandlerComponent in your controller:
public function view() {$article = $this->Article->find('first');$this->response->modified($article['Article']['modified']);if ($this->response->checkNotModified($this->request)) {return $this->response;}...
}

The Vary header
In some cases, you might want to serve different content using the same URL. This is often the case if you have amultilingual page or respond with different HTML depending on the browser. Under such circumstances you can use
the Vary header:$this->response->vary('User-Agent');$this->response->vary('Accept-Encoding', 'User-Agent');$this->response->vary('Accept-Language');

CakeResponse and testing
Probably one of the biggest wins from CakeResponse comes from how it makes testing controllers and components
easier. Instead of having methods spread across several objects, you only have to mock a single object, since controllers
and components delegate to CakeResponse. This helps you to get closer to a unit test and makes testing controllers
easier:public function testSomething() {$this->controller->response = $this->getMock('CakeResponse');$this->controller->response->expects($this->once())->method('header');// ...
}
Additionally, you can run tests from the command line more easily, as you can use mocks to avoid the ‘headers sent’errors that can occur when trying to set headers in CLI.

66

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x

CakeResponse APIclass CakeResponse
CakeResponse provides a number of useful methods for interacting with the response you are sending to a client.
CakeResponse::header($header = null, $value = null)
Allows you to directly set one or more headers to be sent with the response.
CakeResponse::location($url = null)
Allows you to directly set the redirect location header to be sent with the response:// Set the redirect location$this->response->location('http://example.com');// Get the current redirect location header$location = $this->response->location();New in version 2.4.
CakeResponse::charset($charset = null)
Sets the charset that will be used in the response.
CakeResponse::type($contentType = null)
Sets the content type of the response. You can either use a known content type alias or the full content type name.
CakeResponse::cache($since, $time = '+1 day')Allows you to set caching headers in the response.
CakeResponse::disableCache()
Sets the headers to disable client caching for the response.
CakeResponse::sharable($public = null, $time = null)
Sets the Cache-Control header to be either public or private and optionally sets a max-age directive of theresource
New in version 2.1.
CakeResponse::expires($time = null)
Allows the Expires header to be set to a specific date.
New in version 2.1.
CakeResponse::etag($tag = null, $weak = false)
Sets the Etag header to uniquely identify a response resource.
New in version 2.1.
CakeResponse::modified($time = null)
Sets the Last-Modified header to a specific date and time in the correct format.
New in version 2.1.
CakeResponse::checkNotModified(CakeRequest $request)
Compares the cache headers for the request object with the cache header from the response and determines
whether it can still be considered fresh. If so, deletes the response content, and sends the 304 Not Modified
header.
New in version 2.1.

More on controllers

67CakePHP Cookbook Documentation, Release 2.x

CakeResponse::compress()
Turns on gzip compression for the request.
CakeResponse::download($filename)Allows you to send a response as an attachment, and to set its filename.
CakeResponse::statusCode($code = null)
Allows you to set the status code of the response.
CakeResponse::body($content = null)
Sets the content body of the response.
CakeResponse::send()
Once you are done creating a response, calling send() will send all the set headers as well as the body. This isdone automatically at the end of each request by Dispatcher.
CakeResponse::file($path, $options = array())
Allows you to set the Content-Disposition header of a file either to display or to download.
New in version 2.3.

Scaffolding
Deprecated since version 2.5: Dynamic scaffolding will be removed and replaced in 3.0
Application scaffolding is a technique that allows a developer to define and create a basic application that can create,
retrieve, update and delete objects. Scaffolding in CakePHP also allows developers to define how objects are related toeach other, and to create and break those links.
All that’s needed to create a scaffold is a model and its controller. Once you set the $scaffold variable in the controller,
you’re up and running.
CakePHP’s scaffolding is pretty cool. It allows you to get a basic CRUD application up and going in minutes. It’s socool that you’ll want to use it in production apps. Now, we think it’s cool too, but please realize that scaffolding is. . .
well. . . just scaffolding. It’s a loose structure you throw up real quick during the beginning of a project in order to getstarted. It isn’t meant to be completely flexible, it’s meant as a temporary way to get up and going. If you find yourself
really wanting to customize your logic and your views, it’s time to pull your scaffolding down in order to write somecode. CakePHP’s bake console, covered in the next section, is a great next step: it generates all the code that would
produce the same result as the most current scaffold.
Scaffolding is a great way of getting the early parts of developing a web application started. Early database schemas
are subject to change, which is perfectly normal in the early part of the design process. This has a downside: a web
developer hates creating forms that never will see real use. To reduce the strain on the developer, scaffolding has been
included in CakePHP. Scaffolding analyzes your database tables and creates standard lists with add, delete and edit
buttons, standard forms for editing and standard views for inspecting a single item in the database.
To add scaffolding to your application, in the controller, add the $scaffold variable:class CategoriesController extends AppController {public $scaffold;}
Assuming you’ve created even the most basic Category model class file (in app/Model/Category.php), you’re readyto go. Visit http://example.com/categories to see your new scaffold.

68

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x

Note: Creating methods in controllers that are scaffolded can cause unwanted results. For example, if you create anindex() method in a scaffolded controller, your index method will be rendered rather than the scaffolding functionality.
Scaffolding is aware of model’s associations; so, if your Category model belongsTo User, you’ll see related User IDs
in the Category listings. While scaffolding “knows” about model’s associations, you will not see any related records inthe scaffold views until you manually add the association code to the model. For example, if Group hasMany User andUser belongsTo Group, you have to manually add the following code to your User and Group models. Before you doit, the view displays an empty select input for Group in the New User form; after – populated with IDs or names fromthe Group table in the New User form:// In Group.php
public $hasMany = 'User';// In User.php
public $belongsTo = 'Group';
If you’d rather see something besides an ID (like the user’s first name), you can set the $displayField variable in themodel. Let’s set the $displayField variable in our User class so that users related to categories will be shown by
first name rather than just by ID in scaffolding. This feature makes scaffolding more readable in many instances:class User extends AppModel {public $displayField = 'first_name';}

Creating a simple admin interface with scaffolding
If you have enabled admin routing in your app/Config/core.php with Configure::write('Routing.
prefixes', array('admin'));, you can use scaffolding to generate an admin interface.
Once you have enabled admin routing, assign your admin prefix to the scaffolding variable:
public $scaffold = 'admin';
You will now be able to access admin scaffolded actions:
http://example.com/admin/controller/index
http://example.com/admin/controller/view
http://example.com/admin/controller/edit
http://example.com/admin/controller/add
http://example.com/admin/controller/delete
This is an easy way to create a simple backend interface quickly. Keep in mind that you cannot have both admin andnon-admin methods scaffolded at the same time. As with normal scaffolding, you can override individual methods andreplace them with your own:
public function admin_view($id = null) {// custom code here}
Once you have replaced a scaffolded action, you will need to create a view file for the action as well.

More on controllers

69CakePHP Cookbook Documentation, Release 2.x

Customizing Scaffold Views
If you’re looking for something a little different in your scaffolded views, you can create templates. We still don’t
recommend using this technique for production applications, but such a customization may be useful during prototyping
iterations.
Custom scaffolding views for a specific controller (PostsController in this example) should be placed like so:app/View/Posts/scaffold.index.ctp
app/View/Posts/scaffold.form.ctp
app/View/Posts/scaffold.view.ctp
Custom scaffolding views for all controllers should be placed like so:app/View/Scaffolds/index.ctp
app/View/Scaffolds/form.ctp
app/View/Scaffolds/view.ctp

The Pages Controller
CakePHP ships with a default controller PagesController.php. This is a simple and optional controller for serving
up static content. The home page you see after installation is generated using this controller. If you make the view fileapp/View/Pages/about_us.ctp you can access it using the url http://example.com/pages/about_us. You
are free to modify the Pages Controller to meet your needs.
When you “bake” an app using CakePHP’s console utility the Pages Controller is created in your app/Controller/
folder. You can also copy the file from lib/Cake/Console/Templates/skel/Controller/PagesController.
php.
Changed in version 2.1: With CakePHP 2.0 the Pages Controller was part of lib/Cake. Since 2.1 the Pages Controller
is no longer part of the core but ships in the app folder.
Warning: Do not directly modify ANY file under the lib/Cake folder to avoid issues when updating the core in
future.

Components
Components are packages of logic that are shared between controllers. CakePHP comes with a fantastic set of corecomponents you can use to aid in various common tasks. You can also create your own components. If you find yourself
wanting to copy and paste things between controllers, you should consider creating your own component to contain thefunctionality. Creating components keeps controller code clean and allows you to reuse code between projects.
Each of the core components is detailed in its own chapter. See Components. This section describes how to configure
and use components, and how to create your own components.

70

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x

Configuring Components
Many of the core components require configuration. Some examples of components requiring configuration are Authentication and Cookie. Configuration for these components, and for components in general, is usually done in the$components array or your controller’s beforeFilter() method:class PostsController extends AppController {public $components = array('Auth' => array('authorize' => array('controller'),'loginAction' => array('controller' => 'users','action' => 'login')),'Cookie' => array('name' => 'CookieMonster'));
The previous fragment of code would be an example of configuring a component with the $components array. All
core components allow their configuration settings to be set in this way. In addition, you can configure components
in your controller’s beforeFilter() method. This is useful when you need to assign the results of a function to acomponent property. The above could also be expressed as:
public function beforeFilter() {$this->Auth->authorize = array('controller');$this->Auth->loginAction = array('controller' => 'users','action' => 'login');$this->Cookie->name = 'CookieMonster';}It’s possible, however, that a component requires certain configuration options to be set before the controller’s
beforeFilter() is run. To this end, some components allow configuration options be set in the $components array:public $components = array('DebugKit.Toolbar' => array('panels' => array('history', 'session')));Consult the relevant documentation to determine what configuration options each component provides.
One common setting to use is the className option, which allows you to alias components. This feature is usefulwhen you want to replace $this->Auth or another common Component reference with a custom implementation:// app/Controller/PostsController.php
class PostsController extends AppController {public $components = array('Auth' => array('className' => 'MyAuth'));}(continues on next page)

More on controllers

71CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// app/Controller/Component/MyAuthComponent.php
App::uses('AuthComponent', 'Controller/Component');class MyAuthComponent extends AuthComponent {// Add your code to override the core AuthComponent}
The above would alias MyAuthComponent to $this->Auth in your controllers.
Note: Aliasing a component replaces that instance anywhere that component is used, including inside other Components.

Using Components
Once you’ve included some components in your controller, using them is pretty simple. Each component you use is
exposed as a property on your controller. If you had loaded up the SessionComponent and the CookieComponent
in your controller, you could access them like so:class PostsController extends AppController {public $components = array('Session', 'Cookie');public function delete() {if ($this->Post->delete($this->request->data('Post.id'))) {$this->Session->setFlash('Post deleted.');return $this->redirect(array('action' => 'index'));}}Note: Since both Models and Components are added to Controllers as properties they share the same ‘namespace’.
Be sure to not give a component and a model the same name.

Loading components on the fly
You might not need all of your components available on every controller action. In situations like this you can load acomponent at runtime using the Component Collection. From inside a controller’s method you can do the following:$this->OneTimer = $this->Components->load('OneTimer');$this->OneTimer->getTime();Note: Keep in mind that loading a component on the fly will not call its initialize method. If the component you arecalling has this method you will need to call it manually after load.

72

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x

Component Callbacks
Components also offer a few request life-cycle callbacks that allow them to augment the request cycle. See the baseComponent API for more information on the callbacks components offer.
Creating a Component
Suppose our online application needs to perform a complex mathematical operation in many different parts of theapplication. We could create a component to house this shared logic for use in many different controllers.
The first step is to create a new component file and class. Create the file in app/Controller/Component/
MathComponent.php. The basic structure for the component would look something like this:
App::uses('Component', 'Controller');class MathComponent extends Component {public function doComplexOperation($amount1, $amount2) {return $amount1 + $amount2;}}

Note: All components must extend Component. Failing to do this will trigger an exception.

Including your component in your controllers
Once our component is finished, we can use it in the application’s controllers by placing the component’s name (without the “Component” part) in the controller’s $components array. The controller will automatically be given a new
attribute named after the component, through which we can access an instance of it:/* Make the new component available at $this->Math,as well as the standard $this->Session */public $components = array('Math', 'Session');Components declared in AppController will be merged with those in your other controllers. So there is no need tore-declare the same component twice.
When including Components in a Controller you can also declare a set of parameters that will be passed on to theComponent’s constructor. These parameters can then be handled by the Component:
public $components = array('Math' => array('precision' => 2,'randomGenerator' => 'srand'),'Session', 'Auth');
The above would pass the array containing precision and randomGenerator to MathComponent::__construct() asthe second parameter. By convention, if array keys match component’s public properties, the properties will be set tothe values of these keys.

More on controllers

73CakePHP Cookbook Documentation, Release 2.x

Using other Components in your Component
Sometimes one of your components may need to use another component. In this case you can include other components
in your component the exact same way you include them in controllers - using the $components var:// app/Controller/Component/CustomComponent.php
App::uses('Component', 'Controller');class CustomComponent extends Component {// the other component your component uses
public $components = array('Existing');
public function initialize(Controller $controller) {$this->Existing->foo();}public function bar() {// ...
}}// app/Controller/Component/ExistingComponent.php
App::uses('Component', 'Controller');class ExistingComponent extends Component {public function foo() {// ...
}}

Note: In contrast to a component included in a controller no callbacks will be triggered on a component’s component.

Component APIclass Component
The base Component class offers a few methods for lazily loading other Components throughComponentCollection as well as dealing with common handling of settings. It also provides prototypes for
all the component callbacks.
Component::__construct(ComponentCollection $collection, $settings = array())
Constructor for the base component class. All $settings that are also public properties will have their values
changed to the matching value in $settings.

74

Chapter 4. Controllers

CakePHP Cookbook Documentation, Release 2.x

Callbacks
Component::initialize(Controller $controller)
Is called before the controller’s beforeFilter method.
Component::startup(Controller $controller)
Is called after the controller’s beforeFilter method but before the controller executes the current action handler.
Component::beforeRender(Controller $controller)
Is called after the controller executes the requested action’s logic, but before the controller’s renders views andlayout.
Component::shutdown(Controller $controller)
Is called before output is sent to the browser.
Component::beforeRedirect(Controller $controller, $url, $status=null, $exit=true)
Is invoked when the controller’s redirect method is called but before any further action. If this method returns
false the controller will not continue on to redirect the request. The $url, $status and $exit variables have same
meaning as for the controller’s method. You can also return a string which will be interpreted as the URL toredirect to or return an associative array with the key ‘url’ and optionally ‘status’ and ‘exit’.

More on controllers

75CakePHP Cookbook Documentation, Release 2.x

76

Chapter 4. Controllers

CHAPTER 5

Views

Views are the V in MVC. Views are responsible for generating the specific output required for the request. Often
this is in the form of HTML, XML, or JSON, but streaming files and creating PDFs that users can download are also
responsibilities of the View Layer.
CakePHP comes with a few built-in View classes for handling the most common rendering scenarios:• To create XML or JSON webservices you can use the JSON and XML views.
• To serve protected files, or dynamically generated files, you can use Sending files.
• To create multiple themed views, you can use Themes.

View Templates
The view layer of CakePHP is how you speak to your users. Most of the time your views will be showing (X)HTMLdocuments to browsers, but you might also need to serve AMF data to a Flash object, reply to a remote application via
SOAP, or output a CSV file for a user.
By default CakePHP view files are written in plain PHP and have a default extension of .ctp (CakePHP Template).
These files contain all the presentational logic needed to get the data it received from the controller in a format that isready for the audience you’re serving to. If you’d prefer using a templating language like Twig, or Smarty, a subclassof View will bridge your templating language and CakePHP.
A view file is stored in /app/View/, in a subfolder named after the controller that uses the file. It has a filename
corresponding to its action. For example, the view file for the Products controller’s “view()” action would normally befound in /app/View/Products/view.ctp.
The view layer in CakePHP can be made up of a number of different parts. Each part has different uses, and will becovered in this chapter:• views: Views are the part of the page that is unique to the action being run. They form the meat of your application’s response.

77

CakePHP Cookbook Documentation, Release 2.x• elements: smaller, reusable bits of view code. Elements are usually rendered inside views.
• layouts: view files that contain presentational code that wraps many interfaces in your application. Most views
are rendered inside a layout.
• helpers: these classes encapsulate view logic that is needed in many places in the view layer. Among otherthings, helpers in CakePHP can help you build forms, build AJAX functionality, paginate model data, or serve
RSS feeds.

Extending Views
New in version 2.1.
View extending allows you to wrap one view in another. Combining this with view blocks gives you a powerful way tokeep your views DRY . For example, your application has a sidebar that needs to change depending on the specific view
being rendered. By extending a common view file, you can avoid repeating the common markup for your sidebar, andonly define the parts that change:// app/View/Common/view.ctp<h1><?php echo $this->fetch('title'); ?></h1><?php echo $this->fetch('content'); ?><div class="actions"><h3>Related actions</h3><ul><?php echo $this->fetch('sidebar'); ?></ul></div>
The above view file could be used as a parent view. It expects that the view extending it will define the sidebar andtitle blocks. The content block is a special block that CakePHP creates. It will contain all the uncaptured content
from the extending view. Assuming our view file has a $post variable with the data about our post, the view could
look like:<?php// app/View/Posts/view.ctp$this->extend('/Common/view');$this->assign('title', $post);$this->start('sidebar');
?>
<li>
<?php
echo $this->Html->link('edit', array('action' => 'edit',$post['Post']['id'])); ?></li><?php $this->end(); ?>// The remaining content will be available as the 'content' block// in the parent view.
<?php echo h($post['Post']['body']);78

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

The post view above shows how you can extend a view, and populate a set of blocks. Any content not already in adefined block will be captured and put into a special block named content. When a view contains a call to extend(),execution continues to the bottom of the current view file. Once it is complete, the extended view will be rendered.
Calling extend() more than once in a view file will override the parent view that will be processed next:$this->extend('/Common/view');$this->extend('/Common/index');
The above will result in /Common/index.ctp being rendered as the parent view to the current view.
You can nest extended views as many times as necessary. Each view can extend another view if desired. Each parent
view will get the previous view’s content as the content block.
Note: You should avoid using content as a block name in your application. CakePHP uses this for uncaptured content
in extended views.

Using view blocks
New in version 2.1.
View blocks replace $scripts_for_layout and provide a flexible API that allows you to define slots or blocks in yourviews/layouts that will be defined elsewhere. For example, blocks are ideal for implementing things such as sidebars,
or regions to load assets at the bottom/top of the layout. Blocks can be defined in two ways: either as a capturing block,or by direct assignment. The start(), append() and end() methods allow you to work with capturing blocks:// Create the sidebar block.
$this->start('sidebar');echo $this->element('sidebar/recent_topics');
echo $this->element('sidebar/recent_comments');$this->end();// Append into the sidebar later on.
$this->append('sidebar');echo $this->element('sidebar/popular_topics');$this->end();
You can also append into a block using start() multiple times. assign() can be used to clear or overwrite a blockat any time:// Clear the previous content from the sidebar block.
$this->assign('sidebar', '');
In 2.3, a few new methods were added for working with blocks. The prepend() method was added to prepend content
to an existing block:// Prepend to sidebar$this->prepend('sidebar', 'this content goes on top of sidebar');
The method startIfEmpty() can be used to start a block only if it is empty or undefined. If the block already exists,
the captured content will be discarded. This is useful when you want to conditionally define default content for a block
if it does not already exist:

Using view blocks

79CakePHP Cookbook Documentation, Release 2.x// In a view file.
// Create a navbar block$this->startIfEmpty('navbar');echo $this->element('navbar');echo $this->element('notifications');$this->end();// In a parent view/layout<?php $this->startIfEmpty('navbar'); ?><p>If the block is not defined by now - show this instead</p><?php $this->end(); ?>// Somewhere later in the parent view/layout
echo $this->fetch('navbar');In the above example, the navbar block will only contain the content added in the first section. Since the block was
defined in the child view, the default content with the <p> tag will be discarded.
New in version 2.3: startIfEmpty() and prepend() were added in 2.3
Note: You should avoid using content as a block name. This is used by CakePHP internally for extended views, andview content in the layout.

Displaying blocks
New in version 2.1.
You can display blocks using the fetch() method. fetch() will safely output a block, returning ‘’ if a block does
not exist:
echo $this->fetch('sidebar');
You can also use fetch to conditionally show content that should surround a block should it exist. This is helpful inlayouts, or extended views where you want to conditionally show headings or other markup:// In app/View/Layouts/default.ctp<?php if ($this->fetch('menu')): ?><div class="menu"><h3>Menu options</h3><?php echo $this->fetch('menu'); ?></div><?php endif; ?>As of 2.3.0, you can also provide a default value for a block should it not have any content. This allows you to easily
add placeholder content for empty states. You can provide a default value using the second argument:<div class="shopping-cart"><h3>Your Cart</h3><?php echo $this->fetch('cart', 'Your cart is empty'); ?></div>
Changed in version 2.3: The $default argument was added in 2.3.

80

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Using blocks for script and CSS files
New in version 2.1.
Blocks replace the deprecated $scripts_for_layout layout variable. Instead you should use blocks. The
HtmlHelper ties into view blocks, and its script(), css(), and meta() methods each update a block with thesame name when used with the inline = false option:<?php// In your view file
$this->Html->script('carousel', array('inline' => false));$this->Html->css('carousel', array('inline' => false));?>// In your layout file.
<!DOCTYPE html><html lang="en"><head><title><?php echo $this->fetch('title'); ?></title><?php echo $this->fetch('script'); ?><?php echo $this->fetch('css'); ?></head>// Rest of the layout follows
The HtmlHelper also allows you to control which block the scripts and CSS go to:// In your view
$this->Html->script('carousel', array('block' => 'scriptBottom'));// In your layout
echo $this->fetch('scriptBottom');

Layouts
A layout contains presentation code that wraps around a view. Anything you want to see in all of your views should beplaced in a layout.
CakePHP’s default layout is located at /app/View/Layouts/default.ctp. If you want to change the overall lookof your application, then this is the right place to start, because controller-rendered view code is placed inside of thedefault layout when the page is rendered.
Other layout files should be placed in /app/View/Layouts. When you create a layout, you need to tell CakePHP whereto place the output of your views. To do so, make sure your layout includes a place for $this->fetch('content')
Here’s an example of what a default layout might look like:<!DOCTYPE html><html lang="en"><head><title><?php echo $this->fetch('title'); ?></title><link rel="shortcut icon" href="favicon.ico" type="image/x-icon"><!-- Include external files and scripts here (See HTML helper for more info.) --><?php
echo $this->fetch('meta');(continues on next page)

Layouts81

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)echo $this->fetch('css');
echo $this->fetch('script');?></head><body><!-- If you'd like some sort of menu toshow up on all of your views, include it here --><div id="header"><div id="menu">...</div></div><!-- Here's where I want my views to be displayed --><?php echo $this->fetch('content'); ?><!-- Add a footer to each displayed page --><div id="footer">...</div></body></html>Note:
Prior to version 2.1, method fetch() was not available, fetch('content') is a replacement for$content_for_layout and lines fetch('meta'), fetch('css') and fetch('script') are contained in the$scripts_for_layout variable in version 2.0
The script, css and meta blocks contain any content defined in the views using the built-in HTML helper. Usefulfor including JavaScript and CSS files from views.
Note: When using HtmlHelper::css() or HtmlHelper::script() in view files, specify ‘false’ for the ‘inline’
option to place the HTML source in a block with the same name. (See API for more details on usage).
The content block contains the contents of the rendered view.
$title_for_layout contains the page title. This variable is generated automatically, but you can override it by setting
it in your controller/view as you would any other view variable.
Note:
The $title_for_layout is deprecated as of 2.5, use $this->fetch('title') in your layout and$this->assign('title', 'page title') instead.
You can create as many layouts as you wish: just place them in the app/View/Layouts directory, and switch between
them inside of your controller actions using the controller or view’s $layout property:// From a controller
public function admin_view() {// Stuff$this->layout = 'admin';}// From a view file(continues on next page)

82

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$this->layout = 'loggedin';For example, if a section of my site included a smaller ad banner space, I might create a new layout with the smaller
advertising space and specify it as the layout for all controllers’ actions using something like:class UsersController extends AppController {public function view_active() {$this->set('title_for_layout', 'View Active Users');$this->layout = 'default_small_ad';}public function view_image() {$this->layout = 'image';// Output user image}}
CakePHP features two core layouts (besides CakePHP’s default layout) you can use in your own application: ‘ajax’and ‘flash’. The Ajax layout is handy for crafting AJAX responses - it’s an empty layout. (Most AJAX calls only
require a bit of markup in return, rather than a fully-rendered interface.) The flash layout is used for messages shownby Controller::flash() method.
Three other layouts, xml, js, and rss, exist in the core for a quick and easy way to serve up content that isn’t text/html.

Using layouts from plugins
New in version 2.1.
If you want to use a layout that exists in a plugin, you can use plugin syntax. For example, to use the contact layout
from the Contacts plugin:class UsersController extends AppController {public function view_active() {$this->layout = 'Contacts.contact';}}

Elements
Many applications have small blocks of presentation code that need to be repeated from page to page, sometimes
in different places in the layout. CakePHP can help you repeat parts of your website that need to be reused. These
reusable parts are called Elements. Ads, help boxes, navigational controls, extra menus, login forms, and callouts areoften implemented in CakePHP as elements. An element is basically a mini-view that can be included in other views,
in layouts, and even within other elements. Elements can be used to make a view more readable, placing the renderingof repeating elements in its own file. They can also help you re-use content fragments in your application.
Elements live in the /app/View/Elements/ folder, and have the .ctp filename extension. They are output using theelement method of the view:
echo $this->element('helpbox');Elements83CakePHP Cookbook Documentation, Release 2.x

Passing Variables into an Element
You can pass data to an element through the element’s second argument:echo $this->element('helpbox', array("helptext" => "Oh, this text is very helpful."));Inside the element file, all the passed variables are available as members of the parameter array (in the same way thatController::set() in the controller works with view files). In the above example, the /app/View/Elements/
helpbox.ctp file can use the $helptext variable:// Inside app/View/Elements/helpbox.ctp
echo $helptext; // Outputs "Oh, this text is very helpful."
The View::element() method also supports options for the element. The options supported are ‘cache’ and ‘callbacks’. An example:
echo $this->element('helpbox', array("helptext" => "This is passed to the element as $helptext","foobar" => "This is passed to the element as $foobar",),array(// Uses the "long_view" cache configuration"cache" => "long_view",// Set to true to have before/afterRender called for the element"callbacks" => true));
Element caching is facilitated through the Cache class. You can configure elements to be stored in any Cache configuration you’ve set up. This gives you a great amount of flexibility to decide where and for how long elements are stored.
To cache different versions of the same element in an application, provide a unique cache key value using the following
format:$this->element('helpbox', array(), array("cache" => array('config' => 'short', 'key' => 'unique value')));
You can take full advantage of elements by using requestAction(), which fetches view variables from a controller
action and returns them as an array. This enables your elements to perform in true MVC style. Create a controller
action that prepares the view variables for your elements, then call requestAction() inside the second parameter of
element() to feed the element the view variables from your controller.
To do this, in your controller add something like the following for the Post example:
class PostsController extends AppController {// ...
public function index() {$posts = $this->paginate();if ($this->request->is('requested')) {return $posts;}$this->set('posts', $posts);(continues on next page)

84

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}}And then in the element we can access the paginated posts model. To get the latest five posts in an ordered list, we
would do something like the following:<h2>Latest Posts</h2><?php$posts = $this->requestAction('posts/index/sort:created/direction:asc/limit:5'
);
?>
<ol>
<?php foreach ($posts as $post): ?><li><?php echo $post['Post']['title']; ?></li><?php endforeach; ?></ol>Caching Elements
You can take advantage of CakePHP view caching if you supply a cache parameter. If set to true, it will cache theelement in the ‘default’ Cache configuration. Otherwise, you can set which cache configuration should be used. See
Caching for more information on configuring Cache. A simple example of caching an element would be:echo $this->element('helpbox', array(), array('cache' => true));
If you render the same element more than once in a view and have caching enabled, be sure to set the ‘key’ parameterto a different name each time. This will prevent each successive call from overwriting the previous element() call’s
cached result. For example:
echo $this->element('helpbox',
array('var' => $var),array('cache' => array('key' => 'first_use', 'config' => 'view_long'));
echo $this->element('helpbox',
array('var' => $differenVar),array('cache' => array('key' => 'second_use', 'config' => 'view_long'));
The above will ensure that both element results are cached separately. If you want all element caching to use the same
cache configuration, you can avoid some repetition by setting View::$elementCache to the cache configuration you
want to use. CakePHP will use this configuration when none is given.

Elements85

CakePHP Cookbook Documentation, Release 2.x

Requesting Elements from a Plugin
2.0
To load an element from a plugin, use the plugin option (moved out of the data option in 1.x):
echo $this->element('helpbox', array(), array('plugin' => 'Contacts'));2.1
If you are using a plugin and wish to use elements from within the plugin, just use the familiar plugin syntax. If theview is being rendered for a plugin controller/action, the plugin name will automatically be prefixed onto all elements
used, unless another plugin name is present. If the element doesn’t exist in the plugin, it will look in the main APP
folder.
echo $this->element('Contacts.helpbox');
If your view is a part of a plugin, you can omit the plugin name. For example, if you are in the ContactsControllerof the Contacts plugin, the following:echo $this->element('helpbox');// andecho $this->element('Contacts.helpbox');are equivalent and will result in the same element being rendered.
Changed in version 2.1: The $options[plugin] option was deprecated and support for Plugin.element was added.

Creating your own view classes
You may need to create custom view classes to enable new types of data views, or add additional custom view-rendering
logic to your application. Like most components of CakePHP, view classes have a few conventions:• View class files should be put in App/View. For example: App/View/PdfView.php• View classes should be suffixed with View. For example: PdfView.
• When referencing view class names you should omit the View suffix. For example: $this->viewClass ='Pdf';.
You’ll also want to extend View to ensure things work correctly:// In App/View/PdfView.php
App::uses('View', 'View');
class PdfView extends View {public function render($view = null, $layout = null) {// Custom logic here.
}}
Replacing the render method lets you take full control over how your content is rendered.

86

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

View API
class View
View methods are accessible in all view, element and layout files. To call any view method use $this->method()
View::set(string $var, mixed $value)
Views have a set() method that is analogous to the set() found in Controller objects. Using set() from yourview file will add the variables to the layout and elements that will be rendered later. See Controller Methods formore information on using set().
In your view file you can do:$this->set('activeMenuButton', 'posts');
Then, in your layout, the $activeMenuButton variable will be available and contain the value ‘posts’.
View::get(string $var, $default = null)Get the value of a viewVar with the name $var.
As of 2.5, you can provide a default value in case the variable is not already set.
Changed in version 2.5: The $default argument was added in 2.5.
View::getVar(string $var)
Gets the value of the viewVar with the name $var.
Deprecated since version 2.3: Use View::get() instead.
View::getVars()
Gets a list of all the available view variables in the current rendering scope. Returns an array of variable names.
View::element(string $elementPath, array $data, array $options = array())
Renders an element or view partial. See the section on Elements for more information and examples.
View::uuid(string $object, mixed $url)
Generates a unique non-random DOM ID for an object, based on the object type and URL. This method is often
used by helpers that need to generate unique DOM ID’s for elements such as the JsHelper:$uuid = $this->uuid('form',
array('controller' => 'posts', 'action' => 'index'));// $uuid contains 'form0425fe3bad'
View::addScript(string $name, string $content)
Adds content to the internal scripts buffer. This buffer is made available in the layout as $scripts_for_layout.
This method is helpful when creating helpers that need to add javascript or css directly to the layout. Keep inmind that scripts added from the layout and elements in the layout will not be added to $scripts_for_layout.
This method is most often used from inside helpers, such as the JsHelper and HtmlHelper Helpers.
Deprecated since version 2.1: Use the Using view blocks features instead.
View::blocks()
Get the names of all defined blocks as an array.

View API87

CakePHP Cookbook Documentation, Release 2.x

View::start($name)Start a capturing block for a view block. See the section on Using view blocks for examples.
New in version 2.1.
View::end()
End the top most open capturing block. See the section on Using view blocks for examples.
New in version 2.1.
View::append($name, $content)
Append into the block with $name. See the section on Using view blocks for examples.
New in version 2.1.
View::prepend($name, $content)
Prepend to the block with $name. See the section on Using view blocks for examples.
New in version 2.3.
View::startIfEmpty($name)Start a block if it is empty. All content in the block will be captured and discarded if the block is already defined.
New in version 2.3.
View::assign($name, $content)
Assign the value of a block. This will overwrite any existing content. See the section on Using view blocks forexamples.
New in version 2.1.
View::fetch($name, $default = '')Fetch the value of a block. If a block is empty or undefined, ‘’ will be returned. See the section on Using view
blocks for examples.
New in version 2.1.
View::extend($name)
Extend the current view/element/layout with the named one. See the section on Extending Views for examples.
New in version 2.1.
property View::$layout
Set the layout the current view will be wrapped in.
property View::$elementCache
The cache configuration used to cache elements. Setting this property will change the default configuration used
to cache elements. This default can be overridden using the ‘cache’ option in the element method.
property View::$request
An instance of CakeRequest. Use this instance to access information about the current request.
property View::$output
Contains the last rendered content from a view, either the view file, or the layout content.
Deprecated since version 2.1: Use $view->Blocks->get('content'); instead.
property View::$Blocks
An instance of ViewBlock. Used to provide view block functionality in view rendering.
New in version 2.1.

88

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

More about Views
Themes
You can take advantage of themes, making it easy to switch the look and feel of your page quickly and easily.
To use themes, specify the theme name in your controller:class ExampleController extends AppController {public $theme = 'Example';}
Changed in version 2.1: Versions previous to 2.1 required setting the $this->viewClass = 'Theme'. 2.1 removes
this requirement as the normal View class supports themes
You can also set or change the theme name within an action or within the beforeFilter or beforeRender callback
functions:$this->theme = 'AnotherExample';
Theme view files need to be within the /app/View/Themed/ folder. Within the themed folder, create a folder using the same name as your theme name. For example, the above theme would be found in /app/View/Themed/
AnotherExample.
Note: It is important to remember that CakePHP expects CamelCase theme names.
Beyond that, the folder structure within the /app/View/Themed/Example/ folder is exactly the same as /app/View/.
For example, the view file for an edit action of a Posts controller would reside at /app/View/Themed/Example/
Posts/edit.ctp. Layout files would reside in /app/View/Themed/Example/Layouts/.
If a view file can’t be found in the theme, CakePHP will try to locate the view file in the /app/View/ folder. This way,
you can create master view files and simply override them on a case-by-case basis within your theme folder.
Theme assets
Themes can contain static assets as well as view files. A theme can include any necessary assets in its webroot directory.
This allows for easy packaging and distribution of themes. While in development, requests for theme assets will behandled by Dispatcher. To improve performance for production environments, it’s recommended that you either
symlink or copy theme assets into the application’s webroot. See below for more information.
To use the new theme webroot create directories like:app/View/Themed/<themeName>/webroot<path_to_file>in your theme. The Dispatcher will handle finding the correct theme assets in your view paths.
All of CakePHP’s built-in helpers are aware of themes and will create the correct paths automatically. Like view files,
if a file isn’t in the theme folder, it will default to the main webroot folder://When in a theme with the name of 'purple_cupcake'$this->Html->css('main.css');//creates a path like(continues on next page)

More about Views

89CakePHP Cookbook Documentation, Release 2.x(continued from previous page)/theme/purple_cupcake/css/main.css//and links toapp/View/Themed/PurpleCupcake/webroot/css/main.css

Increasing performance of plugin and theme assets
It’s a well known fact that serving assets through PHP is guaranteed to be slower than serving those assets without
invoking PHP. And while the core team has taken steps to make plugin and theme asset serving as fast as possible,
there may be situations where more performance is required. In these situations it’s recommended that you either
symlink or copy out plugin/theme assets to directories in app/webroot with paths matching those used by CakePHP.
• app/Plugin/DebugKit/webroot/js/my_file.js becomes app/webroot/debug_kit/js/my_file.js• app/View/Themed/Navy/webroot/css/navy.css becomes app/webroot/theme/Navy/css/navy.css

Media Views
class MediaView
Deprecated since version 2.3: Use Sending files instead.
Media views allow you to send binary files to the user. For example, you may wish to have a directory of files outsideof the webroot to prevent users from direct linking them. You can use the Media view to pull the file from a special
folder within /app/, allowing you to perform authentication before delivering the file to the user.
To use the Media view, you need to tell your controller to use the MediaView class instead of the default View class.
After that, just pass in additional parameters to specify where your file is located:class ExampleController extends AppController {public function download() {$this->viewClass = 'Media';// Download app/outside_webroot_dir/example.zip$params = array('id'=> 'example.zip','name'=> 'example','download' => true,'extension' => 'zip','path'=> APP . 'outside_webroot_dir' . DS);
$this->set($params);}}
Here’s an example of rendering a file whose mime type is not included in the MediaView’s $mimeType array. We arealso using a relative path which will default to your app/webroot folder:public function download() {$this->viewClass = 'Media';// Render app/webroot/files/example.docx$params = array('id'=> 'example.docx',(continues on next page)

90

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'name'=> 'example','extension' => 'docx','mimeType' => array('docx' => 'application/vnd.openxmlformats-officedocument' .
'.wordprocessingml.document'),'path'=> 'files' . DS);
$this->set($params);}

Settable Parametersid
The ID is the file name as it resides on the file server including the file extension.
name
The name allows you to specify an alternate file name to be sent to the user. Specify the name without the fileextension.
download
A boolean value indicating whether headers should be set to force download.
extension
The file extension. This is matched against an internal list of acceptable mime types. If the mime type specified
is not in the list (or set in the mimeType parameter array), the file will not be downloaded.
path
The folder name, including the final directory separator. The path should be absolute but can be relative to theapp/webroot folder.
mimeType
An array with additional mime types to be merged with MediaView internal list of acceptable mime types.
cache
A boolean or integer value - If set to true it will allow browsers to cache the file (defaults to false if not set);otherwise set it to the number of seconds in the future for when the cache should expire.

JSON and XML views
New in CakePHP 2.1 are two new view classes. The XmlView and JsonView let you easily create XML and JSON
responses, and integrate with the RequestHandlerComponent.
By enabling RequestHandlerComponent in your application, and enabling support for the xml and or json extensions, you can automatically leverage the new view classes. XmlView and JsonView will be referred to as data views
for the rest of this page.
There are two ways you can generate data views. The first is by using the _serialize key, and the second is by creating
normal view files.

More about Views

91CakePHP Cookbook Documentation, Release 2.x

Enabling data views in your application
Before you can use the data view classes, you’ll need to do a bit of setup:
1. Enable the json and or xml extensions with Router::parseExtensions(). This will enable Router to handle
multiple extensions.
2. Add the RequestHandlerComponent to your controller’s list of components. This will enable automatic viewclass switching on content types. You can also set the component up with the viewClassMap setting, to map
types to your custom classes and/or map other data types.
New in version 2.3: RequestHandlerComponent::viewClassMap() method has been added to map types to viewClasses. The viewClassMap setting will not work on earlier versions.
After adding Router::parseExtensions('json'); to your routes file, CakePHP will automatically switch viewclasses when a request is done with the .json extension, or the Accept header is application/json.
Using data views with the serialize key
The _serialize key is a special view variable that indicates which other view variable(s) should be serialized whenusing a data view. This lets you skip defining view files for your controller actions if you don’t need to do any custom
formatting before your data is converted into json/xml.
If you need to do any formatting or manipulation of your view variables before generating the response, you should use
view files. The value of _serialize can be either a string or an array of view variables to serialize:
class PostsController extends AppController {public $components = array('RequestHandler');
public function index() {$this->set('posts', $this->Paginator->paginate());$this->set('_serialize', array('posts'));}}
You can also define _serialize as an array of view variables to combine:class PostsController extends AppController {public $components = array('RequestHandler');
public function index() {// some code that created $posts and $comments$this->set(compact('posts', 'comments'));$this->set('_serialize', array('posts', 'comments'));}}Defining _serialize as an array has the added benefit of automatically appending a top-level <response> element
when using XmlView. If you use a string value for _serialize and XmlView, make sure that your view variable hasa single top-level element. Without a single top-level element the Xml will fail to generate.

92

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Using a data view with view files
You should use view files if you need to do some manipulation of your view content before creating the final output.
For example if we had posts, that had a field containing generated HTML, we would probably want to omit that froma JSON response. This is a situation where a view file would be useful:// Controller codeclass PostsController extends AppController {public function index() {$this->set(compact('posts', 'comments'));}}// View code - app/View/Posts/json/index.ctp
foreach ($posts as &$post) {unset($post['Post']['generated_html']);}echo json_encode(compact('posts', 'comments'));
You can do more complex manipulations, or use helpers to do formatting as well.
Note: The data view classes don’t support layouts. They assume that the view file will output the serialized content.
class XmlView
A view class for generating Xml view data. See above for how you can use XmlView in your application.
By default when using _serialize the XmlView will wrap your serialized view variables with a <response>
node. You can set a custom name for this node using the _rootNode view variable.
New in version 2.3: The _rootNode feature was added.
New in version 2.6: The XmlView class supports the _xmlOptions variable that allows you to customize theoptions used to generate XML, e.g. tags vs attributes.
class JsonView
A view class for generating Json view data. See above for how you can use JsonView in your application.
New in version 2.6: JsonView now supports the _jsonOptions view variable. This allows you to configure the
bit-mask options used when generating JSON.
JSONP response
New in version 2.4.
When using JsonView you can use the special view variable _jsonp to enable returning a JSONP response. Setting it
to true makes the view class check if query string parameter named “callback” is set and if so wrap the json responsein the function name provided. If you want to use a custom query string parameter name instead of “callback” set_jsonp to required name instead of true.

More about Views

93CakePHP Cookbook Documentation, Release 2.x

Helpers
Helpers are the component-like classes for the presentation layer of your application. They contain presentational logic
that is shared between many views, elements, or layouts. This chapter will show you how to create your own helpers,
and outline the basic tasks CakePHP’s core helpers can help you accomplish.
CakePHP features a number of helpers that aid in view creation. They assist in creating well-formed markup (including
forms), aid in formatting text, times and numbers, and can even speed up AJAX functionality. For more informationon the helpers included in CakePHP, check out the chapter for each helper:
CacheHelperclass CacheHelper(View $view, array $settings = array())
The Cache helper assists in caching entire layouts and views, saving time repetitively retrieving data. View Caching in
CakePHP temporarily stores parsed layouts and views as simple PHP + HTML files. It should be noted that the Cachehelper works quite differently than other helpers. It does not have methods that are directly called. Instead, a view ismarked with cache tags indicating which blocks of content should not be cached. The CacheHelper then uses helper
callbacks to process the file and output to generate the cache file.
When a URL is requested, CakePHP checks to see if that request string has already been cached. If it has, the rest of the
URL dispatching process is skipped. Any nocache blocks are processed normally and the view is served. This createsa big savings in processing time for each request to a cached URL as minimal code is executed. If CakePHP doesn’t
find a cached view, or the cache has expired for the requested URL it continues to process the request normally.
Using the Helper
There are two steps you have to take before you can use the CacheHelper. First in your APP/Config/core.php
uncomment the Configure write call for Cache.check. This will tell CakePHP to check for, and generate view cache
files when handling requests.
Once you’ve uncommented the Cache.check line you will need to add the helper to your controller’s $helpers array:class PostsController extends AppController {public $helpers = array('Cache');}
You will also need to add the CacheDispatcher to your dispatcher filters in your bootstrap:
Configure::write('Dispatcher.filters', array('CacheDispatcher'));
New in version 2.3: If you have a setup with multiple domains or languages you can use Configure::write(‘Cache.viewPrefix’, ‘YOURPREFIX’); to store the view cache files prefixed.

94

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Additional configuration options
CacheHelper has a few additional configuration options you can use to tune and tweak its behavior. This is done throughthe $cacheAction variable in your controllers. $cacheAction should be set to an array which contains the actions
you want cached, and the duration in seconds you want those views cached. The time value can be expressed in a
strtotime() format (e.g. “1 hour”, or “3 minutes”).
Using the example of an ArticlesController, that receives a lot of traffic that needs to be cached:public $cacheAction = array('view' => 36000,'index' => 48000);
This will cache the view action 10 hours, and the index action 13 hours. By making $cacheAction a strtotime()friendly value you can cache every action in the controller:
public $cacheAction = "1 hour";
You can also enable controller/component callbacks for cached views created with CacheHelper. To do so you must
use the array format for $cacheAction and create an array like the following:
public $cacheAction = array('view' => array('callbacks' => true, 'duration' => 21600),'add' => array('callbacks' => true, 'duration' => 36000),'index' => array('callbacks' => true, 'duration' => 48000));
By setting callbacks => true you tell CacheHelper that you want the generated files to create the components andmodels for the controller. Additionally, fire the component initialize, controller beforeFilter, and component startup
callbacks.
Note: Setting callbacks => true partly defeats the purpose of caching. This is also the reason it is disabled bydefault.

Marking Non-Cached Content in Views
There will be times when you don’t want an entire view cached. For example, certain parts of the page may lookdifferent whether a user is currently logged in or browsing your site as a guest.
To indicate blocks of content that are not to be cached, wrap them in <!--nocache--> <!--/nocache--> like so:<!--nocache--><?php if ($this->Session->check('User.name')): ?>Welcome, <?php echo h($this->Session->read('User.name')); ?>.
<?php else: ?><?php echo $this->Html->link('Login', 'users/login'); ?><?php endif; ?><!--/nocache-->Note: You cannot use nocache tags in elements. Since there are no callbacks around elements, they cannot be cached.

More about Views

95CakePHP Cookbook Documentation, Release 2.x

It should be noted that once an action is cached, the controller method for the action will not be called. When a cache
file is created, the request object, and view variables are serialized with PHP’s serialize().
Warning: If you have view variables that contain un-serializable content such as SimpleXML objects, resource
handles, or closures you might not be able to use view caching.

Clearing the Cache
It is important to remember that CakePHP will clear a cached view if a model used in the cached view is modified. For
example, if a cached view uses data from the Post model, and there has been an INSERT, UPDATE, or DELETE query
made to a Post, the cache for that view is cleared, and new content is generated on the next request.
Note: This automatic cache clearing requires the controller/model name to be part of the URL. If you’ve used routing
to change your URLs this feature will not work.
If you need to manually clear the cache, you can do so by calling Cache::clear(). This will clear all cached data,
excluding cached view files. If you need to clear the cached view files, use clearCache().
FlashHelperclass FlashHelper(View $view, array $config = array())
New in version 2.7.0: in replacement of SessionHelper::flash()FlashHelper provides a way to render flash messages that were set in $_SESSION by FlashComponent. FlashComponent and FlashHelper primarily use elements to render flash messages. Flash elements are found under the app/View/
Elements/Flash directory. You’ll notice that CakePHP’s App template comes with two flash elements: success.
ctp and error.ctp.
The FlashHelper replaces the flash() method on SessionHelper and should be used instead of that method.
Rendering Flash Messages
To render a flash message, you can simply use FlashHelper’s render() method:<?php echo $this->Flash->render() ?>By default, CakePHP uses a “flash” key for flash messages in a session. But, if you’ve specified a key when setting the
flash message in FlashComponent, you can specify which flash key to render:<?php echo $this->Flash->render('other') ?>You can also override any of the options that were set in FlashComponent:// In your Controller$this->Flash->set('The user has been saved.', array('element' => 'success'));// In your View: Will use great_success.ctp instead of success.ctp(continues on next page)

96

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)<?php echo $this->Flash->render('flash', array('element' => 'great_success'));Note: By default, CakePHP does not escape the HTML in flash messages. If you are using any request or user data inyour flash messages, you should escape it with h when formatting your messages.
New in version 2.10.0: Flash stacks messages as of 2.10.0. If you set multiple flash messages, when you call render(),each message will be rendered in its own element, in the order the messages were set.
For more information about the available array options, please refer to the FlashComponent section.
FormHelperclass FormHelper(View $view, array $settings = array())
The FormHelper does most of the heavy lifting in form creation. The FormHelper focuses on creating forms quickly,
in a way that will streamline validation, re-population and layout. The FormHelper is also flexible - it will do almost
everything for you using conventions, or you can use specific methods to get only what you need.
Creating Forms
The first method you’ll need to use in order to take advantage of the FormHelper is create(). This special method
outputs an opening form tag.
FormHelper::create(string $model = null, array $options = array())
All parameters are optional. If create() is called with no parameters supplied, it assumes you are building
a form that submits to the current controller, via the current URL. The default method for form submission is
POST. The form element is also returned with a DOM ID. The ID is generated using the name of the model, andthe name of the controller action, CamelCased. If I were to call create() inside a UsersController view, I’d see
something like the following output in the rendered view:<form id="UserAddForm" method="post" action="/users/add">

Note:
You can also pass false for $model.
This will place your form data into the array:$this->request->data (instead of in the sub-array: $this->request->data['Model']). This can be
handy for short forms that may not represent anything in your database.
The create() method allows us to customize much more using the parameters, however. First, you can specify
a model name. By specifying a model for a form, you are creating that form’s context. All fields are assumed tobelong to this model (unless otherwise specified), and all models referenced are assumed to be associated with
it. If you do not specify a model, then it assumes you are using the default model for the current controller:// If you are on /recipes/add
echo $this->Form->create('Recipe');
Output:<form id="RecipeAddForm" method="post" action="/recipes/add">

More about Views

97CakePHP Cookbook Documentation, Release 2.x

This will POST the form data to the add() action of RecipesController. However, you can also use the same
logic to create an edit form. The FormHelper uses the $this->request->data property to automatically detect
whether to create an add or edit form. If $this->request->data contains an array element named after theform’s model, and that array contains a non-empty value of the model’s primary key, then the FormHelper will
create an edit form for that record. For example, if we browse to https://site.com/recipes/edit/5, we would get
the following:// Controller/RecipesController.php:
public function edit($id = null) {if (empty($this->request->data)) {$this->request->data = $this->Recipe->findById($id);} else {// Save logic goes here}}// View/Recipes/edit.ctp:// Since $this->request->data['Recipe']['id'] = 5,// we will get an edit form<?php echo $this->Form->create('Recipe'); ?>Output:<form id="RecipeEditForm" method="post" action="/recipes/edit/5"><input type="hidden" name="_method" value="PUT" />Note: Since this is an edit form, a hidden input field is generated to override the default HTTP method.
When creating forms for models in plugins, you should always use plugin syntax when creating a form. This will
ensure the form is correctly generated:echo $this->Form->create('ContactManager.Contact');
The $options array is where most of the form configuration happens. This special array can contain a numberof different key-value pairs that affect the way the form tag is generated.
Changed in version 2.0: The default URL for all forms, is now the current URL including passed, named, andquerystring parameters. You can override this default by supplying $options['url'] in the second parameterof $this->Form->create().
Options for create()
There are a number of options for create():• $options['type'] This key is used to specify the type of form to be created. Valid values include ‘post’, ‘get’,‘file’, ‘put’ and ‘delete’.
Supplying either ‘post’ or ‘get’ changes the form submission method accordingly:
echo $this->Form->create('User', array('type' => 'get'));Output:98

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x<form id="UserAddForm" method="get" action="/users/add">
Specifying ‘file’ changes the form submission method to ‘post’, and includes an enctype of “multipart/form-data”
on the form tag. This is to be used if there are any file elements inside the form. The absence of the proper enctype
attribute will cause the file uploads not to function:echo $this->Form->create('User', array('type' => 'file'));Output:<form id="UserAddForm" enctype="multipart/form-data"
method="post" action="/users/add">
When using ‘put’ or ‘delete’, your form will be functionally equivalent to a ‘post’ form, but when submitted,
the HTTP request method will be overridden with ‘PUT’ or ‘DELETE’, respectively. This allows CakePHP toemulate proper REST support in web browsers.
• $options['action'] The action key allows you to point the form to a specific action in your current controller.
For example, if you’d like to point the form to the login() action of the current controller, you would supply an$options array like the following:echo $this->Form->create('User', array('action' => 'login'));Output:<form id="UserLoginForm" method="post" action="/users/login">
Deprecated since version 2.8.0: The $options['action'] option was deprecated as of 2.8.0. Use the$options['url'] and $options['id'] options instead.
• $options['url'] If the desired form action isn’t in the current controller, you can specify a URL for the form
action using the ‘url’ key of the $options array. The supplied URL can be relative to your CakePHP application:echo $this->Form->create(false, array('url' => array('controller' => 'recipes', 'action' => 'add'),'id' => 'RecipesAdd'));
Output:<form method="post" action="/recipes/add">
or can point to an external domain:echo $this->Form->create(false, array('url' => 'https://www.google.com/search','type' => 'get'));
Output:<form method="get" action="https://www.google.com/search">
Also check HtmlHelper::url() method for more examples of different types of URLs.
Changed in version 2.8.0: Use 'url' => false if you don’t want to output a URL as the form action.

More about Views

99CakePHP Cookbook Documentation, Release 2.x• $options['default'] If ‘default’ has been set to boolean false, the form’s submit action is changed so thatpressing the submit button does not submit the form. If the form is meant to be submitted via AJAX, setting‘default’ to false suppresses the form’s default behavior so you can grab the data and submit it via AJAX instead.
• $options['inputDefaults'] You can declare a set of default options for input() with the inputDefaults
key to customize your default input creation:echo $this->Form->create('User', array('inputDefaults' => array('label' => false,'div' => false)));
All inputs created from that point forward would inherit the options declared in inputDefaults. You can override
the defaultOptions by declaring the option in the input() call:echo $this->Form->input('password'); // No div, no label// has a label element
echo $this->Form->input('username',array('label' => 'Username'));

Closing the Form
FormHelper::end($options = null, $secureAttributes = array())
The FormHelper includes an end() method that completes the form. Often, end() only outputs a closing form tag, but using end() also allows the FormHelper to insert needed hidden form elements that
SecurityComponent requires:<?php echo $this->Form->create(); ?><!-- Form elements go here --><?php echo $this->Form->end(); ?>If a string is supplied as the first parameter to end(), the FormHelper outputs a submit button named accordingly
along with the closing form tag:<?php echo $this->Form->end('Finish'); ?>Will output:<div class="submit"><input type="submit" value="Finish" /></div></form>You can specify detail settings by passing an array to end():$options = array('label' => 'Update',(continues on next page)

100

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'div' => array('class' => 'glass-pill',));echo $this->Form->end($options);
Will output:<div class="glass-pill"><input type="submit" value="Update" name="Update"></div>
See the Form Helper API55 for further details.
Note: If you are using SecurityComponent in your application you should always end your forms with end().
Changed in version 2.5: The $secureAttributes parameter was added in 2.5.
Creating form elements
There are a few ways to create form inputs with the FormHelper. We’ll start by looking at input(). This method willautomatically inspect the model field it has been supplied in order to create an appropriate input for that field. Internallyinput() delegates to other methods in FormHelper.
FormHelper::input(string $fieldName, array $options = array())
Creates the following elements given a particular Model.field:• Wrapping div.
• Label element• Input element(s)• Error element with message if applicable.
The type of input created depends on the column datatype:
Column Type
Resulting Form Field
string (char, varchar, etc.)
text
boolean, tinyint(1)checkbox
text
textarea
text, with name of password, passwd, or psword
password
text, with name of email
email
text, with name of tel, telephone, or phonetel
55 https://api.cakephp.org/2.x/class-FormHelper.html

More about Views

101

CakePHP Cookbook Documentation, Release 2.x

dateday, month, and year selects
datetime, timestampday, month, year, hour, minute, and meridian selects
timehour, minute, and meridian selects
binaryfile
The $options parameter allows you to customize how input() works, and finely control what is generated.
The wrapping div will have a required class name appended if the validation rules for the Model’s field do
not specify allowEmpty => true. One limitation of this behavior is the field’s model must have been loaded
during this request. Or be directly associated to the model supplied to create().
New in version 2.5: The binary type now maps to a file input.
New in version 2.3. Since 2.3 the HTML5 required attribute will also be added to the input based on validation
rules. You can explicitly set required key in options array to override it for a field. To skip browser validation
triggering for the whole form you can set option 'formnovalidate' => true for the input button you generateusing FormHelper::submit() or set 'novalidate' => true in options for FormHelper::create().
For example, let’s assume that your User model includes fields for a username (varchar), password (varchar),approved (datetime) and quote (text). You can use the input() method of the FormHelper to create appropriate
inputs for all of these form fields:
echo $this->Form->create();echo $this->Form->input('username');echo $this->Form->input('password');echo $this->Form->input('approved');echo $this->Form->input('quote');//text//password//day, month, year, hour, minute,//meridian//textareaecho $this->Form->end('Add');
A more extensive example showing some options for a date field:
echo $this->Form->input('birth_dt', array('label' => 'Date of birth','dateFormat' => 'DMY','minYear' => date('Y') - 70,'maxYear' => date('Y') - 18,));
Besides the specific options for input() found below, you can specify any option for the input type & any HTMLattribute (for instance onfocus). For more information on $options and $htmlAttributes see HtmlHelper.
Assuming that User hasAndBelongsToMany Group. In your controller, set a camelCase plural variable (group ->groups in this case, or ExtraFunkyModel -> extraFunkyModels) with the select options. In the controller action
you would put the following:$this->set('groups', $this->User->Group->find('list'));And in the view a multiple select can be created with this simple code:

102

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

echo $this->Form->input('Group', array('multiple' => true));
If you want to create a select field while using a belongsTo - or hasOne - Relation, you can add the following to
your Users-controller (assuming your User belongsTo Group):$this->set('groups', $this->User->Group->find('list'));Afterwards, add the following to your form-view:
echo $this->Form->input('group_id');If your model name consists of two or more words, e.g., “UserGroup”, when passing the data using set() you
should name your data in a pluralised and camelCased format as follows:$this->set('userGroups', $this->UserGroup->find('list'));// or$this->set('reallyInappropriateModelNames',$this->ReallyInappropriateModelName->find('list'));

Note: Try to avoid using FormHelper::input() to generate submit buttons. Use FormHelper::submit()
instead.
FormHelper::inputs(mixed $fields = null, array $blacklist = null, $options = array())
Generate a set of inputs for $fields. If $fields is null all fields, except of those defined in $blacklist, ofthe current model will be used.
In addition to controller fields output, $fields can be used to control legend and fieldset rendering with thefieldset and legend keys. $this->Form->inputs(array('legend' => 'My legend')); Would generate an input set with a custom legend. You can customize individual inputs through $fields as well.
echo $this->Form->inputs(array('name' => array('label' => 'custom label')));
In addition to fields control, inputs() allows you to use a few additional options.
• fieldset Set to false to disable the fieldset. If a string is supplied it will be used as the class name for thefieldset element.
• legend Set to false to disable the legend for the generated input set. Or supply a string to customize thelegend text.

More about Views

103CakePHP Cookbook Documentation, Release 2.x

Field naming conventions
The Form helper is pretty smart. Whenever you specify a field name with the form helper methods, it’ll automaticallyuse the current model name to build an input with a format like the following:<input type="text" id="ModelnameFieldname" name="data[Modelname][fieldname]">This allows you to omit the model name when generating inputs for the model that the form was created for. You cancreate inputs for associated models, or arbitrary models by passing in Modelname.fieldname as the first parameter:
echo $this->Form->input('Modelname.fieldname');If you need to specify multiple fields using the same field name, thus creating an array that can be saved in one shotwith saveAll(), use the following convention:echo $this->Form->input('Modelname.0.fieldname');echo $this->Form->input('Modelname.1.fieldname');
Output:<input type="text" id="Modelname0Fieldname"name="data[Modelname][0][fieldname]"><input type="text" id="Modelname1Fieldname"name="data[Modelname][1][fieldname]">FormHelper uses several field-suffixes internally for datetime input creation. If you are using fields named year, month,day, hour, minute, or meridian and having issues getting the correct input, you can set the name attribute to override
the default behavior:echo $this->Form->input('Model.year', array('type' => 'text','name' => 'data[Model][year]'));

Options
FormHelper::input() supports a large number of options. In addition to its own options input() accepts options for the generated input types, as well as HTML attributes. The following will cover the options specific to
FormHelper::input().
• $options['type'] You can force the type of an input, overriding model introspection, by specifying a type.
In addition to the field types found in the Creating form elements, you can also create ‘file’, ‘password’, and any
type supported by HTML5:
echo $this->Form->input('field', array('type' => 'file'));echo $this->Form->input('email', array('type' => 'email'));Output:<div class="input file"><label for="UserField">Field</label><input type="file" name="data[User][field]" value="" id="UserField" /></div>(continues on next page)

104

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)<div class="input email"><label for="UserEmail">Email</label><input type="email" name="data[User][email]" value="" id="UserEmail" /></div>• $options['div'] Use this option to set attributes of the input’s containing div. Using a string value will set the
div’s class name. An array will set the div’s attributes to those specified by the array’s keys/values. Alternatively,
you can set this key to false to disable the output of the div.
Setting the class name:echo $this->Form->input('User.name', array('div' => 'class_name'));Output:<div class="class_name"><label for="UserName">Name</label><input name="data[User][name]" type="text" value="" id="UserName" /></div>
Setting multiple attributes:
echo $this->Form->input('User.name', array('div' => array('id' => 'mainDiv','title' => 'Div Title','style' => 'display:block')));
Output:<div class="input text" id="mainDiv" title="Div Title"
style="display:block"><label for="UserName">Name</label><input name="data[User][name]" type="text" value="" id="UserName" /></div>Disabling div output:
echo $this->Form->input('User.name', array('div' => false)); ?>Output:<label for="UserName">Name</label><input name="data[User][name]" type="text" value="" id="UserName" />• $options['label'] Set this key to the string you would like to be displayed within the label that usuallyaccompanies the input:

More about Views

105

CakePHP Cookbook Documentation, Release 2.x

echo $this->Form->input('User.name', array('label' => 'The User Alias'));
Output:<div class="input"><label for="UserName">The User Alias</label><input name="data[User][name]" type="text" value="" id="UserName" /></div>
Alternatively, set this key to false to disable the output of the label:echo $this->Form->input('User.name', array('label' => false));
Output:<div class="input"><input name="data[User][name]" type="text" value="" id="UserName" /></div>
Set this to an array to provide additional options for the label element. If you do this, you can use a text key
in the array to customize the label text:
echo $this->Form->input('User.name', array('label' => array('class' => 'thingy','text' => 'The User Alias')));
Output:<div class="input"><label for="UserName" class="thingy">The User Alias</label><input name="data[User][name]" type="text" value="" id="UserName" /></div>• $options['error'] Using this key allows you to override the default model error messages and can be used,
for example, to set i18n messages. It has a number of suboptions which control the wrapping element, wrapping
element class name, and whether HTML in the error message will be escaped.
To disable error message output & field classes set the error key to false:$this->Form->input('Model.field', array('error' => false));To disable only the error message, but retain the field classes, set the errorMessage key to false:$this->Form->input('Model.field', array('errorMessage' => false));To modify the wrapping element type and its class, use the following format:$this->Form->input('Model.field', array('error' => array('attributes' => array('wrap' => 'span', 'class' => 'bzzz')(continues on next page)

106

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page))));
To prevent HTML being automatically escaped in the error message output, set the escape suboption to false:$this->Form->input('Model.field', array('error' => array('attributes' => array('escape' => false))));
To override the model error messages use an array with the keys matching the validation rule names:$this->Form->input('Model.field', array('error' => array('tooShort' => __('This is not long enough'))));
As seen above you can set the error message for each validation rule you have in your models. In addition you
can provide i18n messages for your forms.
New in version 2.3: Support for the errorMessage option was added in 2.3• $options['before'], $options['between'], $options['separator'], and $options['after']
Use these keys if you need to inject some markup inside the output of the input() method:echo $this->Form->input('field', array('before' => '--before--','after' => '--after--','between' => '--between---'));Output:<div class="input">--before-<label for="UserField">Field</label>--between--<input name="data[User][field]" type="text" value="" id="UserField" />--after-</div>
For radio inputs the ‘separator’ attribute can be used to inject markup to separate each input/label pair:echo $this->Form->input('field', array('before' => '--before--','after' => '--after--','between' => '--between---','separator' => '--separator--','options' => array('1', '2'),'type' => 'radio'));
Output:More about Views

107CakePHP Cookbook Documentation, Release 2.x<div class="input">--before-<input name="data[User][field]" type="radio" value="1" id="UserField1" /><label for="UserField1">1</label>--separator-<input name="data[User][field]" type="radio" value="2" id="UserField2" /><label for="UserField2">2</label>--between----after-</div>
For date and datetime type elements the ‘separator’ attribute can be used to change the string between selectelements. Defaults to ‘-‘.
• $options['format'] The ordering of the HTML generated by FormHelper is controllable as well. The ‘format’ options supports an array of strings describing the template you would like said element to follow. The
supported array keys are: array('before', 'input', 'between', 'label', 'after','error').
• $options['inputDefaults'] If you find yourself repeating the same options in multiple input() calls, you
can use inputDefaults` to keep your code dry:echo $this->Form->create('User', array('inputDefaults' => array('label' => false,'div' => false)));
All inputs created from that point forward would inherit the options declared in inputDefaults. You can override
the defaultOptions by declaring the option in the input() call:// No div, no label
echo $this->Form->input('password');// has a label element
echo $this->Form->input('username', array('label' => 'Username'));If you need to later change the defaults you can use FormHelper::inputDefaults().
• $options['maxlength'] Set this key to set the maxlength attribute of the input field to a specific value.
When this key is omitted and the input-type is text, textarea, email, tel, url or search and the fielddefinition is not one of decimal, time or datetime, the length option of the database field is used.
GET Form Inputs
When using FormHelper to generate inputs for GET forms, the input names will automatically be shortened to provide
more human friendly names. For example:// Makes <input name="email" type="text" />echo $this->Form->input('User.email');// Makes <select name="Tags" multiple="multiple">echo $this->Form->input('Tags.Tags', array('multiple' => true));
If you want to override the generated name attributes you can use the name option:
108

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x// Makes the more typical <input name="data[User][email]" type="text" />echo $this->Form->input('User.email', array('name' => 'data[User][email]'));

Generating specific types of inputs
In addition to the generic input() method, FormHelper has specific methods for generating a number of different
types of inputs. These can be used to generate just the input widget itself, and combined with other methods likelabel() and error() to generate fully custom form layouts.
Common options
Many of the various input element methods support a common set of options. All of these options are also supported
by input(). To reduce repetition the common options shared by all input methods are as follows:• $options['class'] You can set the class name for an input:
echo $this->Form->input('title', array('class' => 'custom-class'));• $options['id'] Set this key to force the value of the DOM id for the input.
• $options['default'] Used to set a default value for the input field. The value is used if the data passed to
the form does not contain a value for the field (or if no data is passed at all).
Example usage:
echo $this->Form->input('ingredient', array('default' => 'Sugar'));Example with select field (Size “Medium” will be selected as default):$sizes = array('s' => 'Small', 'm' => 'Medium', 'l' => 'Large');echo $this->Form->input('size',
array('options' => $sizes, 'default' => 'm')
);

Note:
You cannot use default to check a checkbox - instead you might set the value in$this->request->data in your controller, or set the input option checked to true.
Date and datetime fields’ default values can be set by using the ‘selected’ key.
Beware of using false to assign a default value. A false value is used to disable/exclude options of an input field,
so 'default' => false would not set any value at all. Instead use 'default' => 0.
In addition to the above options, you can mixin any HTML attribute you wish to use. Any non-special option namewill be treated as an HTML attribute, and applied to the generated HTML input element.

More about Views

109

CakePHP Cookbook Documentation, Release 2.x

Options for select, checkbox and radio inputs• $options['selected'] Used in combination with a select-type input (i.e. For types select, date, time, datetime). Set ‘selected’ to the value of the item you wish to be selected by default when the input is rendered:echo $this->Form->input('close_time', array('type' => 'time','selected' => '13:30:00'));

Note: The selected key for date and datetime inputs may also be a UNIX timestamp.
• $options['empty'] If set to true, forces the input to remain empty.
When passed to a select list, this creates a blank option with an empty value in your drop down list. If you wantto have a empty value with text displayed instead of just a blank option, pass in a string to empty:
echo $this->Form->input('field', array('options' => array(1, 2, 3, 4, 5),'empty' => '(choose one)'));
Output:<div class="input"><label for="UserField">Field</label><select name="data[User][field]" id="UserField"><option value="">(choose one)</option><option value="0">1</option><option value="1">2</option><option value="2">3</option><option value="3">4</option><option value="4">5</option></select></div>

Note: If you need to set the default value in a password field to blank, use ‘value’ => ‘’ instead.
A list of key-value pairs can be supplied for a date or datetime field:
echo $this->Form->dateTime('Contact.date', 'DMY', '12',
array('empty' => array('day' => 'DAY', 'month' => 'MONTH', 'year' => 'YEAR','hour' => 'HOUR', 'minute' => 'MINUTE', 'meridian' => false)));
Output:110

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x<select name="data[Contact][date][day]" id="ContactDateDay"><option value="">DAY</option><option value="01">1</option>// ...
<option value="31">31</option></select> - <select name="data[Contact][date][month]" id="ContactDateMonth"><option value="">MONTH</option><option value="01">January</option>// ...
<option value="12">December</option></select> - <select name="data[Contact][date][year]" id="ContactDateYear"><option value="">YEAR</option><option value="2036">2036</option>// ...
<option value="1996">1996</option></select> <select name="data[Contact][date][hour]" id="ContactDateHour"><option value="">HOUR</option><option value="01">1</option>// ...
<option value="12">12</option></select>:<select name="data[Contact][date][min]" id="ContactDateMin"><option value="">MINUTE</option><option value="00">00</option>// ...
<option value="59">59</option></select> <select name="data[Contact][date][meridian]" id="ContactDateMeridian"><option value="am">am</option><option value="pm">pm</option></select>• $options['hiddenField'] For certain input types (checkboxes, radios) a hidden input is created so that the
key in $this->request->data will exist even without a value specified:<input type="hidden" name="data[Post][Published]" id="PostPublished_"value="0" /><input type="checkbox" name="data[Post][Published]" value="1"
id="PostPublished" />This can be disabled by setting the $options['hiddenField'] = false:
echo $this->Form->checkbox('published', array('hiddenField' => false));Which outputs:<input type="checkbox" name="data[Post][Published]" value="1"
id="PostPublished" />If you want to create multiple blocks of inputs on a form that are all grouped together, you should use thisparameter on all inputs except the first. If the hidden input is on the page in multiple places, only the last groupof input’s values will be saved
In this example, only the tertiary colors would be passed, and the primary colors would be overridden:

More about Views

111

CakePHP Cookbook Documentation, Release 2.x<h2>Primary Colors</h2><input type="hidden" name="data[Color][Color]" id="Colors_" value="0" /><input type="checkbox" name="data[Color][Color][]" value="5"
id="ColorsRed" /><label for="ColorsRed">Red</label><input type="checkbox" name="data[Color][Color][]" value="5"
id="ColorsBlue" /><label for="ColorsBlue">Blue</label><input type="checkbox" name="data[Color][Color][]" value="5"
id="ColorsYellow" /><label for="ColorsYellow">Yellow</label><h2>Tertiary Colors</h2><input type="hidden" name="data[Color][Color]" id="Colors_" value="0" /><input type="checkbox" name="data[Color][Color][]" value="5"
id="ColorsGreen" /><label for="ColorsGreen">Green</label><input type="checkbox" name="data[Color][Color][]" value="5"
id="ColorsPurple" /><label for="ColorsPurple">Purple</label><input type="checkbox" name="data[Addon][Addon][]" value="5"
id="ColorsOrange" /><label for="ColorsOrange">Orange</label>
Disabling the 'hiddenField' on the second input group would prevent this behavior.
You can set a different hidden field value other than 0 such as ‘N’:echo $this->Form->checkbox('published', array('value' => 'Y',
'hiddenField' => 'N',));

Datetime options• $options['timeFormat'] Used to specify the format of the select inputs for a time-related set of inputs. Validvalues include 12, 24, and null.
• $options['dateFormat'] Used to specify the format of the select inputs for a date-related set of inputs. Validvalues include any combination of ‘D’, ‘M’ and ‘Y’ or null. The inputs will be put in the order defined by thedateFormat option.
• $options['minYear'], $options['maxYear'] Used in combination with a date/datetime input. Defines
the lower and/or upper end of values shown in the years select field.
• $options['orderYear'] Used in combination with a date/datetime input. Defines the order in which the year
values will be set. Valid values include ‘asc’, ‘desc’. The default value is ‘desc’.
• $options['interval'] This option specifies the number of minutes between each option in the minutes selectbox:
echo $this->Form->input('Model.time', array('type' => 'time',(continues on next page)

112

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'interval' => 15));
Would create 4 options in the minute select. One for each 15 minutes.
• $options['round'] Can be set to up or down to force rounding in either direction. Defaults to null whichrounds half up according to interval.
New in version 2.4.
Form Element-Specific Methods
All elements are created under a form for the User model as in the examples above. For this reason, the HTML code
generated will contain attributes that reference to the User model. Ex: name=data[User][username], id=UserUsername
FormHelper::label(string $fieldName, string $text, array $options)
Create a label element. $fieldName is used for generating the DOM id. If $text is undefined, $fieldName
will be used to inflect the label’s text:
echo $this->Form->label('User.name');echo $this->Form->label('User.name', 'Your username');
Output:<label for="UserName">Name</label><label for="UserName">Your username</label>$options can either be an array of HTML attributes, or a string that will be used as a class name:echo $this->Form->label('User.name', null, array('id' => 'user-label'));echo $this->Form->label('User.name', 'Your username', 'highlight');
Output:<label for="UserName" id="user-label">Name</label><label for="UserName" class="highlight">Your username</label>
FormHelper::text(string $name, array $options)
The rest of the methods available in the FormHelper are for creating specific form elements. Many of thesemethods also make use of a special $options parameter. In this case, however, $options is used primarily to
specify HTML tag attributes (such as the value or DOM id of an element in the form):echo $this->Form->text('username', array('class' => 'users'));Will output:<input name="data[User][username]" type="text" class="users"
id="UserUsername" />FormHelper::password(string $fieldName, array $options)
Creates a password field.

More about Views

113

CakePHP Cookbook Documentation, Release 2.x

echo $this->Form->password('password');Will output:<input name="data[User][password]" value="" id="UserPassword"
type="password" />FormHelper::hidden(string $fieldName, array $options)
Creates a hidden form input. Example:
echo $this->Form->hidden('id');
Will output:<input name="data[User][id]" id="UserId" type="hidden" />If the form is edited (that is, the array $this->request->data will contain the information saved for the Usermodel), the value corresponding to id field will automatically be added to the HTML generated. Example fordata[User][id] = 10:<input name="data[User][id]" id="UserId" type="hidden" value="10" />Changed in version 2.0: Hidden fields no longer remove the class attribute. This means that if there are validation
errors on hidden fields, the error-field class name will be applied.
FormHelper::textarea(string $fieldName, array $options)
Creates a textarea input field.
echo $this->Form->textarea('notes');Will output:<textarea name="data[User][notes]" id="UserNotes"></textarea>
If the form is edited (that is, the array $this->request->data will contain the information saved for the Usermodel), the value corresponding to notes field will automatically be added to the HTML generated. Example:<textarea name="data[User][notes]" id="UserNotes">
This text is to be edited.
</textarea>

Note: The textarea input type allows for the $options attribute of 'escape' which determines whether or
not the contents of the textarea should be escaped. Defaults to true.
echo $this->Form->textarea('notes', array('escape' => false);// OR....
echo $this->Form->input('notes',array('type' => 'textarea', 'escape' => false));
Options
In addition to the Common options, textarea() supports a few specific options:114

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x• $options['rows'], $options['cols'] These two keys specify the number of rows and columns:
echo $this->Form->textarea('textarea',array('rows' => '5', 'cols' => '5'));
Output:<textarea name="data[Form][textarea]" cols="5" rows="5" id="FormTextarea"></textarea>
FormHelper::checkbox(string $fieldName, array $options)
Creates a checkbox form element. This method also generates an associated hidden form input to force thesubmission of data for the specified field.
echo $this->Form->checkbox('done');Will output:<input type="hidden" name="data[User][done]" value="0" id="UserDone_" /><input type="checkbox" name="data[User][done]" value="1" id="UserDone" />It is possible to specify the value of the checkbox by using the $options array:echo $this->Form->checkbox('done', array('value' => 555));
Will output:<input type="hidden" name="data[User][done]" value="0" id="UserDone_" /><input type="checkbox" name="data[User][done]" value="555" id="UserDone" />If you don’t want the Form helper to create a hidden input:
echo $this->Form->checkbox('done', array('hiddenField' => false));Will output:<input type="checkbox" name="data[User][done]" value="1" id="UserDone" />FormHelper::radio(string $fieldName, array $options, array $attributes)
Creates a set of radio button inputs.
Options• $attributes['value'] to set which value should be selected default.
• $attributes['separator'] to specify HTML in between radio buttons (e.g. <br />).
• $attributes['between'] specify some content to be inserted between the legend and first element.
• $attributes['disabled'] Setting this to true or 'disabled' will disable all of the generated radio
buttons.
• $attributes['legend'] Radio elements are wrapped with a legend and fieldset by default.
$attributes['legend'] to false to remove them.

More about Views

Set

115

CakePHP Cookbook Documentation, Release 2.x$options = array('M' => 'Male', 'F' => 'Female');$attributes = array('legend' => false);
echo $this->Form->radio('gender', $options, $attributes);Will output:<input name="data[User][gender]" id="UserGender_" value=""
type="hidden" /><input name="data[User][gender]" id="UserGenderM" value="M"
type="radio" /><label for="UserGenderM">Male</label><input name="data[User][gender]" id="UserGenderF" value="F"
type="radio" /><label for="UserGenderF">Female</label>
If for some reason you don’t want the hidden input, setting $attributes['value'] to a selected value orboolean false will do just that.
• $attributes['fieldset'] If legend attribute is not set to false, then this attribute can be used to set theclass of the fieldset element.
Changed in version 2.1: The $attributes['disabled'] option was added in 2.1.
Changed in version 2.8.5: The $attributes['fieldset'] option was added in 2.8.5.
FormHelper::select(string $fieldName, array $options, array $attributes)
Creates a select element, populated with the items in $options, with the option specified by$attributes['value'] shown as selected by default. Set the ‘empty’ key in the $attributes variable tofalse to turn off the default empty option:$options = array('M' => 'Male', 'F' => 'Female');echo $this->Form->select('gender', $options);
Will output:<select name="data[User][gender]" id="UserGender"><option value=""></option><option value="M">Male</option><option value="F">Female</option></select>
The select input type allows for a special $option attribute called 'escape' which accepts a bool and determines whether to HTML entity encode the contents of the select options. Defaults to true:$options = array('M' => 'Male', 'F' => 'Female');echo $this->Form->select('gender', $options, array('escape' => false));• $attributes['options'] This key allows you to manually specify options for a select input, or for aradio group. Unless the ‘type’ is specified as ‘radio’, the FormHelper will assume that the target output isa select input:
echo $this->Form->select('field', array(1,2,3,4,5));
Output:116

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x<select name="data[User][field]" id="UserField"><option value="0">1</option><option value="1">2</option><option value="2">3</option><option value="3">4</option><option value="4">5</option></select>
Options can also be supplied as key-value pairs:echo $this->Form->select('field', array('Value 1' => 'Label 1','Value 2' => 'Label 2','Value 3' => 'Label 3'));
Output:<select name="data[User][field]" id="UserField"><option value=""></option><option value="Value 1">Label 1</option><option value="Value 2">Label 2</option><option value="Value 3">Label 3</option></select>
If you would like to generate a select with optgroups, just pass data in hierarchical format. This works on
multiple checkboxes and radio buttons too, but instead of optgroups wraps elements in fieldsets:$options = array('Group 1' => array('Value 1' => 'Label 1','Value 2' => 'Label 2'),'Group 2' => array('Value 3' => 'Label 3'));
echo $this->Form->select('field', $options);
Output:<select name="data[User][field]" id="UserField"><optgroup label="Group 1"><option value="Value 1">Label 1</option><option value="Value 2">Label 2</option></optgroup><optgroup label="Group 2"><option value="Value 3">Label 3</option></optgroup></select>• $attributes['multiple'] If ‘multiple’ has been set to true for an input that outputs a select, the selectwill allow multiple selections:

More about Views

117

CakePHP Cookbook Documentation, Release 2.x

echo $this->Form->select('Model.field',$options,array('multiple' => true));
Alternatively set ‘multiple’ to ‘checkbox’ to output a list of related check boxes:$options = array('Value 1' => 'Label 1','Value 2' => 'Label 2');
echo $this->Form->select('Model.field', $options, array('multiple' => 'checkbox'));
Output:<div class="input select"><label for="ModelField">Field</label><input name="data[Model][field]" value="" id="ModelField"
type="hidden"><div class="checkbox"><input name="data[Model][field][]" value="Value 1"
id="ModelField1" type="checkbox"><label for="ModelField1">Label 1</label></div><div class="checkbox"><input name="data[Model][field][]" value="Value 2"
id="ModelField2" type="checkbox"><label for="ModelField2">Label 2</label></div></div>• $attributes['disabled'] When creating checkboxes, this option can be set to disable all or somecheckboxes. To disable all checkboxes set disabled to true:$options = array('Value 1' => 'Label 1','Value 2' => 'Label 2');
echo $this->Form->select('Model.field', $options, array('multiple' => 'checkbox','disabled' => array('Value 1')));
Output:<div class="input select"><label for="ModelField">Field</label><input name="data[Model][field]" value="" id="ModelField"
type="hidden"><div class="checkbox">(continues on next page)

118

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)<input name="data[Model][field][]" disabled="disabled"value="Value 1" id="ModelField1" type="checkbox"><label for="ModelField1">Label 1</label></div><div class="checkbox"><input name="data[Model][field][]" value="Value 2"
id="ModelField2" type="checkbox"><label for="ModelField2">Label 2</label></div></div>• $attributes['showParents'] When creating selects with optgroups this will ensure an element will
be added for the parent of each option group:$options = array('First' => array('Label 1', 'Label 2'),
'Second' => array('Second', 'Third'),);echo $this->Form->select('Model.field', $options, array('showParents' => true));
Output:<select name="data[Model][field]" id="ModelField"><optgroup label="First"><option value="0">Label 1</option><option value="1">Label 2</option></optgroup><optgroup label="Second"><option value="0">Second</option><option value="1">Third</option></optgroup></select>
Changed in version 2.3: Support for arrays in $attributes['disabled'] was added in 2.3.
FormHelper::file(string $fieldName, array $options)
To add a file upload field to a form, you must first make sure that the form enctype is set to “multipart/form-data”,
so start off with a create function such as the following:echo $this->Form->create('Document', array('enctype' => 'multipart/form-data'
));
// OR
echo $this->Form->create('Document', array('type' => 'file'));Next add either of the two lines to your form view file:echo $this->Form->input('Document.submittedfile', array('between' => '<br />',
'type' => 'file'));(continues on next page)

More about Views

119CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// OR
echo $this->Form->file('Document.submittedfile');Due to the limitations of HTML itself, it is not possible to put default values into input fields of type ‘file’. Eachtime the form is displayed, the value inside will be empty.
Upon submission, file fields provide an expanded data array to the script receiving the form data.
For the example above, the values in the submitted data array would be organized as follows, if the CakePHP was
installed on a Windows server. ‘tmp_name’ will have a different path in a Unix environment:$this->request->data['Document']['submittedfile'] = array('name' => 'conference_schedule.pdf','type' => 'application/pdf','tmp_name' => 'C:/WINDOWS/TEMP/php1EE.tmp','error' => 0,'size' => 41737,);
This array is generated by PHP itself, so for more detail on the way PHP handles data passed via file fields readthe PHP manual section on file uploads56 .
Validating Uploads
Below is an example validation method you could define in your model to validate whether a file has been successfully
uploaded:public function isUploadedFile($params) {$val = array_shift($params);
if ((isset($val['error']) && $val['error'] == 0) ||(!empty( $val['tmp_name']) && $val['tmp_name'] != 'none')) {return is_uploaded_file($val['tmp_name']);}return false;}
Creates a file input:
echo $this->Form->create('User', array('type' => 'file'));echo $this->Form->file('avatar');Will output:<form enctype="multipart/form-data" method="post" action="/users/add"><input name="data[User][avatar]" value="" id="UserAvatar" type="file">

Note: When using $this->Form->file(), remember to set the form encoding-type, by setting the type option to
56 https://www.php.net/features.file-upload

120

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x‘file’ in $this->Form->create()

Creating buttons and submit elements
FormHelper::submit(string $caption, array $options)
Creates a submit button with caption $caption. If the supplied $caption is a URL to an image (it contains a‘.’ character), the submit button will be rendered as an image.
It is enclosed between div tags by default; you can avoid this by declaring $options['div'] = false:
echo $this->Form->submit();Will output:<div class="submit"><input value="Submit" type="submit"></div>
You can also pass a relative or absolute URL to an image for the caption parameter instead of caption text.
echo $this->Form->submit('ok.png');
Will output:<div class="submit"><input type="image" src="/img/ok.png"></div>
FormHelper::button(string $title, array $options = array())
Creates an HTML button with the specified title and a default type of “button”. Setting $options['type']
will output one of the three possible button types:
1. submit: Same as the $this->Form->submit method - (the default).
2. reset: Creates a form reset button.
3. button: Creates a standard push button.
echo $this->Form->button('A Button');echo $this->Form->button('Another Button', array('type' => 'button'));echo $this->Form->button('Reset the Form', array('type' => 'reset'));echo $this->Form->button('Submit Form', array('type' => 'submit'));Will output:<button type="submit">A Button</button><button type="button">Another Button</button><button type="reset">Reset the Form</button><button type="submit">Submit Form</button>
The button input type supports the escape option, which accepts a bool and determines whether to HTML
entity encode the $title of the button. Defaults to false:
echo $this->Form->button('Submit Form', array('type' => 'submit','escape' => true));More about Views

121CakePHP Cookbook Documentation, Release 2.x

FormHelper::postButton(string $title, mixed $url, array $options = array ())
Create a <button> tag with a surrounding <form> that submits via POST.
This method creates a <form> element. So do not use this method in some opened form. Instead use
FormHelper::submit() or FormHelper::button() to create buttons inside opened forms.
FormHelper::postLink(string $title, mixed $url = null, array $options = array ())
Creates an HTML link, but access the URL using method POST. Requires JavaScript to be enabled in browser.
This method creates a <form> element. If you want to use this method inside of an existing form, you must use
the inline or block options so that the new form can be rendered outside of the existing form.
If all you are looking for is a button to submit your form, then you should use FormHelper::submit() instead.
Changed in version 2.3: The method option was added.
Changed in version 2.5: The inline and block options were added. They allow buffering the generated form
tag, instead of returning with the link. This helps avoiding nested form tags. Setting 'inline' => false will
add the form tag to the postLink content block, if you want to use a custom block you can specify it using theblock option instead.
Changed in version 2.6: The argument $confirmMessage was deprecated. Use confirm key in $options
instead.
Creating date and time inputs
FormHelper::dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $attributes = array())
Creates a set of select inputs for date and time. Valid values for $dateformat are ‘DMY’, ‘MDY’, ‘YMD’ or‘NONE’. Valid values for $timeFormat are ‘12’, ‘24’, and null.
You can specify not to display empty values by setting “array(‘empty’ => false)” in the attributes parameter. It
will also pre-select the fields with the current datetime.
FormHelper::year(string $fieldName, int $minYear, int $maxYear, array $attributes)
Creates a select element populated with the years from $minYear to $maxYear. HTML attributes may besupplied in $attributes. If $attributes['empty'] is false, the select will not include an empty option:echo $this->Form->year('purchased', 2000, date('Y'));
Will output:<select name="data[User][purchased][year]" id="UserPurchasedYear"><option value=""></option><option value="2009">2009</option><option value="2008">2008</option><option value="2007">2007</option><option value="2006">2006</option><option value="2005">2005</option><option value="2004">2004</option><option value="2003">2003</option><option value="2002">2002</option><option value="2001">2001</option><option value="2000">2000</option></select>

122

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

FormHelper::month(string $fieldName, array $attributes)
Creates a select element populated with month names:
echo $this->Form->month('mob');
Will output:<select name="data[User][mob][month]" id="UserMobMonth"><option value=""></option><option value="01">January</option><option value="02">February</option><option value="03">March</option><option value="04">April</option><option value="05">May</option><option value="06">June</option><option value="07">July</option><option value="08">August</option><option value="09">September</option><option value="10">October</option><option value="11">November</option><option value="12">December</option></select>
You can pass in your own array of months to be used by setting the ‘monthNames’ attribute, or have months
displayed as numbers by passing false. (Note: the default months are internationalized and can be translatedusing localization.):echo $this->Form->month('mob', array('monthNames' => false));FormHelper::day(string $fieldName, array $attributes)
Creates a select element populated with the (numerical) days of the month.
To create an empty option with prompt text of your choosing (e.g. the first option is ‘Day’), you can supply the
text as the final parameter as follows:echo $this->Form->day('created');Will output:<select name="data[User][created][day]" id="UserCreatedDay"><option value=""></option><option value="01">1</option><option value="02">2</option><option value="03">3</option>...
<option value="31">31</option></select>
FormHelper::hour(string $fieldName, boolean $format24Hours, array $attributes)
Creates a select element populated with the hours of the day.
FormHelper::minute(string $fieldName, array $attributes)
Creates a select element populated with the minutes of the hour.
FormHelper::meridian(string $fieldName, array $attributes)
Creates a select element populated with ‘am’ and ‘pm’.
More about Views

123CakePHP Cookbook Documentation, Release 2.x

Displaying and checking errors
FormHelper::error(string $fieldName, mixed $text, array $options)Shows a validation error message, specified by $text, for the given field, in the event that a validation error has
occurred.
Options:• ‘escape’ bool Whether or not to HTML escape the contents of the error.
• ‘wrap’ mixed Whether or not the error message should be wrapped in a div. If a string, will be used as theHTML tag to use.
• ‘class’ string The class name for the error message
FormHelper::isFieldError(string $fieldName)
Returns true if the supplied $fieldName has an active validation error.
if ($this->Form->isFieldError('gender')) {echo $this->Form->error('gender');}

Note: When using FormHelper::input(), errors are rendered by default.
FormHelper::tagIsInvalid()
Returns false if given form field described by the current entity has no errors. Otherwise it returns the validation
message.
Setting Defaults for all fields
New in version 2.2.
You can declare a set of default options for input() using FormHelper::inputDefaults(). Changing the default
options allows you to consolidate repeated options into a single method call:$this->Form->inputDefaults(array('label' => false,'div' => false,'class' => 'fancy'));
All inputs created from that point forward will inherit the options declared in inputDefaults. You can override thedefault options by declaring the option in the input() call:echo $this->Form->input('password'); // No div, no label with class 'fancy'// has a label element same defaults
echo $this->Form->input('username',array('label' => 'Username'));

124

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Working with SecurityComponent
SecurityComponent offers several features that make your forms safer and more secure. By simply including the
SecurityComponent in your controller, you’ll automatically benefit from CSRF and form tampering features.
As mentioned previously when using SecurityComponent, you should always close your forms using
FormHelper::end(). This will ensure that the special _Token inputs are generated.
FormHelper::unlockField($name)
Unlocks a field making it exempt from the SecurityComponent field hashing. This also allows the fields to be
manipulated by JavaScript. The $name parameter should be the entity name for the input:$this->Form->unlockField('User.id');
FormHelper::secure(array $fields = array())
Generates a hidden field with a security hash based on the fields used in the form.
2.0 updates$selected parameter removed
The $selected parameter was removed from several methods in FormHelper. All methods now support a$attributes['value'] key now which should be used in place of $selected. This change simplifies theFormHelper methods, reducing the number of arguments, and reduces the duplication that $selected created. The
effected methods are:
• FormHelper::select()• FormHelper::dateTime()• FormHelper::year()• FormHelper::month()• FormHelper::day()• FormHelper::hour()• FormHelper::minute()• FormHelper::meridian()
Default URLs on forms is the current action
The default URL for all forms, is now the current URL including passed, named, and querystring parameters. You canoverride this default by supplying $options['url'] in the second parameter of $this->Form->create()
FormHelper::hidden()
Hidden fields no longer remove the class attribute. This means that if there are validation errors on hidden fields, theerror-field class name will be applied.

More about Views

125CakePHP Cookbook Documentation, Release 2.x

HtmlHelperclass HtmlHelper(View $view, array $settings = array())
The role of the HtmlHelper in CakePHP is to make HTML-related options easier, faster, and more resilient to change.
Using this helper will enable your application to be more light on its feet, and more flexible on where it is placed inrelation to the root of a domain.
Many HtmlHelper methods include a $options parameter, that allow you to tack on any extra attributes on your tags.
Here are a few examples of how to use the $options parameter:
Desired attributes: <tag class="someClass" />Array parameter: array('class' => 'someClass')
Desired attributes: <tag name="foo" value="bar" />Array parameter: array('name' => 'foo', 'value' => 'bar')
Note: The HtmlHelper is available in all views by default. If you’re getting an error informing you that it isn’t there,
it’s usually due to its name being missing from a manually configured $helpers controller variable.

Inserting Well-Formatted elements
The most important task the HtmlHelper accomplishes is creating well formed markup. Don’t be afraid to use it often- you can cache views in CakePHP in order to save some CPU cycles when views are being rendered and delivered.
This section will cover some of the methods of the HtmlHelper and how to use them.
HtmlHelper::charset($charset=null)Parameters• $charset (string) – Desired character set. If null, the value of App.encoding will be
used.
Used to create a meta tag specifying the document’s character. Defaults to UTF-8
Example use:echo $this->Html->charset();Will output:<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />Alternatively,
echo $this->Html->charset('ISO-8859-1');
Will output:<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />HtmlHelper::css(mixed $path, array $options = array())
Changed in version 2.4.
Parameters

126

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x• $path (mixed) – Either a string of the CSS file to link, or an array with multiple files• $options (array) – An array of options or html attributes.
Creates a link(s) to a CSS style-sheet. If key ‘inline’ is set to false in $options parameter, the link tags are added
to the css block which you can print inside the head tag of the document.
You can use the block option to control which block the link element will be appended to. By default it will
append to the css block.
If key ‘rel’ in $options array is set to ‘import’ the stylesheet will be imported.
This method of CSS inclusion assumes that the CSS file specified resides inside the /app/webroot/css directory
if path doesn’t start with a ‘/’.
echo $this->Html->css('forms');
Will output:<link rel="stylesheet" type="text/css" href="/css/forms.css" />The first parameter can be an array to include multiple files.
echo $this->Html->css(array('forms', 'tables', 'menu'));Will output:<link rel="stylesheet" type="text/css" href="/css/forms.css" /><link rel="stylesheet" type="text/css" href="/css/tables.css" /><link rel="stylesheet" type="text/css" href="/css/menu.css" />You can include CSS files from any loaded plugin using plugin syntax. To include app/Plugin/DebugKit/
webroot/css/toolbar.css you could use the following:echo $this->Html->css('DebugKit.toolbar.css');If you want to include a CSS file which shares a name with a loaded plugin you can do the following. For example
if you had a Blog plugin, and also wanted to include app/webroot/css/Blog.common.css, you would:
Changed in version 2.4.
echo $this->Html->css('Blog.common.css', array('plugin' => false));Changed in version 2.1: The block option was added. Support for plugin syntax was added.
HtmlHelper::meta(string $type, string $url = null, array $options = array())
Parameters• $type (string) – The type meta tag you want.
• $url (mixed) – The URL for the meta tag, either a string or a routing array.
• $options (array) – An array of html attributes.
This method is handy for linking to external resources like RSS/Atom feeds and favicons. Like css(), you canspecify whether or not you’d like this tag to appear inline or appended to the meta block by setting the ‘inline’
key in the $options parameter to false, ie - array('inline' => false).
If you set the “type” attribute using the $options parameter, CakePHP contains a few shortcuts:

More about Views

127CakePHP Cookbook Documentation, Release 2.x

typetranslated value

html
rss
atom
icon

text/html
application/rss+xml
application/atom+xml
image/x-icon<?phpecho $this->Html->meta('favicon.ico','/favicon.ico',array('type' => 'icon'));?>// Output (line breaks added)<link
href="http://example.com/favicon.ico"
title="favicon.ico" type="image/x-icon"
rel="alternate"/><?php
echo $this->Html->meta('Comments','/comments/index.rss',array('type' => 'rss'));?>// Output (line breaks added)<link
href="http://example.com/comments/index.rss"
title="Comments"
type="application/rss+xml"
rel="alternate"/>This method can also be used to add the meta keywords and descriptions. Example:<?php
echo $this->Html->meta('keywords','enter any meta keyword here');?>// Output<meta name="keywords" content="enter any meta keyword here" /><?phpecho $this->Html->meta('description','enter any meta description here');?>(continues on next page)

128

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// Output<meta name="description" content="enter any meta description here" />If you want to add a custom meta tag then the first parameter should be set to an array. To output a robots noindex
tag use the following code:echo $this->Html->meta(array('name' => 'robots', 'content' => 'noindex'));Changed in version 2.1: The block option was added.
HtmlHelper::docType(string $type = 'xhtml-strict')
Parameters• $type (string) – The type of doctype being made.
Returns a (X)HTML doctype tag. Supply the doctype according to the following table:
type

translated value

html4-strict
html4-transhtml4-frame
html5
xhtml-strict
xhtml-transxhtml-frame
xhtml11

HTML4 Strict
HTML4 Transitional
HTML4 Frameset
HTML5
XHTML1 Strict
XHTML1 Transitional
XHTML1 Frameset
XHTML1.1

echo $this->Html->docType();// Outputs:// <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"//"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">echo $this->Html->docType('html5');// Outputs: <!DOCTYPE html>
echo $this->Html->docType('html4-trans');// Outputs:// <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"//"http://www.w3.org/TR/html4/loose.dtd">
Changed in version 2.1: The default doctype is html5 in 2.1.
HtmlHelper::style(array $data, boolean $oneline = true)
Parameters• $data (array) – A set of key => values with CSS properties.
• $oneline (boolean) – Should the contents be on one line.
Builds CSS style definitions based on the keys and values of the array passed to the method. Especially handy ifyour CSS file is dynamic.

More about Views

129CakePHP Cookbook Documentation, Release 2.x

echo $this->Html->style(array('background' => '#633','border-bottom' => '1px solid #000','padding' => '10px'));
Will output:
background:#633; border-bottom:1px solid #000; padding:10px;
HtmlHelper::image(string $path, array $options = array())
Parameters• $path (string) – Path to the image.
• $options (array) – An array of html attributes.
Creates a formatted image tag. The path supplied should be relative to /app/webroot/img/.
echo $this->Html->image('cake_logo.png', array('alt' => 'CakePHP'));
Will output:<img src="/img/cake_logo.png" alt="CakePHP" />To create an image link specify the link destination using the url option in $options.
echo $this->Html->image("recipes/6.jpg", array("alt" => "Brownies",'url' => array('controller' => 'recipes', 'action' => 'view', 6)));Will output:<a href="/recipes/view/6"><img src="/img/recipes/6.jpg" alt="Brownies" /></a>
If you are creating images in emails, or want absolute paths to images you can use the fullBase option:echo $this->Html->image("logo.png", array('fullBase' => true));Will output:<img src="http://example.com/img/logo.jpg" alt="" />You can include image files from any loaded plugin using plugin syntax. To include app/Plugin/DebugKit/
webroot/img/icon.png You could use the following:echo $this->Html->image('DebugKit.icon.png');
If you want to include an image file which shares a name with a loaded plugin you can do the following. For
example if you had a Blog plugin, and also wanted to include app/webroot/img/Blog.icon.png, you would:
echo $this->Html->image('Blog.icon.png', array('plugin' => false));130

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Changed in version 2.1: The fullBase option was added. Support for plugin syntax was added.
HtmlHelper::link(string $title, mixed $url = null, array $options = array())
Parameters• $title (string) – The text to display as the body of the link.
• $url (mixed) – Either the string location, or a routing array.
• $options (array) – An array of html attributes.
General purpose method for creating HTML links. Use $options to specify attributes for the element andwhether or not the $title should be escaped.
echo $this->Html->link('Enter','/pages/home',array('class' => 'button', 'target' => '_blank'));
Will output:<a href="/pages/home" class="button" target="_blank">Enter</a>
Use 'full_base' => true option for absolute URLs:
echo $this->Html->link('Dashboard',
array('controller' => 'dashboards','action' => 'index','full_base' => true));
Will output:<a href="http://www.yourdomain.com/dashboards/index">Dashboard</a>Specify confirm key in $options to display a JavaScript confirm() dialog:
echo $this->Html->link('Delete',array('controller' => 'recipes', 'action' => 'delete', 6),
array('confirm' => 'Are you sure you wish to delete this recipe?'));Will output:<a href="/recipes/delete/6"
onclick="return confirm('Are you sure you wish to delete this recipe?');">
Delete</a>Query strings can also be created with link().
More about Views

131CakePHP Cookbook Documentation, Release 2.x

echo $this->Html->link('View image', array('controller' => 'images','action' => 'view',
1,'?' => array('height' => 400, 'width' => 500)));
Will output:<a href="/images/view/1?height=400&width=500">View image</a>When using named parameters, use the array syntax and include names for ALL parameters in the URL. Using
the string syntax for paramters (i.e. “recipes/view/6/comments:false”) will result in the colon characters being
HTML escaped and the link will not work as desired.
<?php
echo $this->Html->link($this->Html->image("recipes/6.jpg", array("alt" => "Brownies")),array('controller' => 'recipes','action' => 'view','id' => 6,'comments' => false));
Will output:<a href="/recipes/view/id:6/comments:false"><img src="/img/recipes/6.jpg" alt="Brownies" /></a>HTML special characters in $title will be converted to HTML entities. To disable this conversion, set theescape option to false in the $options array.
<?phpecho $this->Html->link($this->Html->image("recipes/6.jpg", array("alt" => "Brownies")),"recipes/view/6",
array('escape' => false));
Will output:<a href="/recipes/view/6"><img src="/img/recipes/6.jpg" alt="Brownies" /></a>Setting escape to false will also disable escaping of attributes of the link. As of 2.4 you can use the option
escapeTitle to disable just escaping of title and not the attributes.
<?php
echo $this->Html->link($this->Html->image('recipes/6.jpg', array('alt' => 'Brownies')),(continues on next page)

132

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'recipes/view/6',
array('escapeTitle' => false, 'title' => 'hi "howdy"'));
Will output:<a href="/recipes/view/6" title="hi &quot;howdy&quot;"><img src="/img/recipes/6.jpg" alt="Brownies" /></a>Changed in version 2.4: The escapeTitle option was added.
Changed in version 2.6: The argument $confirmMessage was deprecated. Use confirm key in $options
instead.
Also check HtmlHelper::url method for more examples of different types of URLs.
HtmlHelper::media(string|array $path, array $options)Parameters• $path (string|array) – Path to the media file, relative to the webroot/{$options[‘pathPrefix’]} directory. Or an array where each item itself can be apath string or an associate array containing keys src and type.
• $options (array) – Array of HTML attributes, and special options.
Options:• type Type of media element to generate, valid values are “audio” or “video”. If type is not provided media
type is guessed based on file’s mime type.
• text Text to include inside the audio/video tag• pathPrefix Path prefix to use for relative URLs, defaults to ‘files/’• fullBase If set to true, the src attribute will get a full address including domain name
New in version 2.1.
Returns a formatted audio/video tag:<?php echo $this->Html->media('audio.mp3'); ?>// Output<audio src="/files/audio.mp3"></audio><?php echo $this->Html->media('video.mp4', array('fullBase' => true,'text' => 'Fallback text')); ?>// Output<video src="http://www.somehost.com/files/video.mp4">Fallback text</video><?php echo $this->Html->media(
array('video.mp4',
array((continues on next page)

More about Views

133CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'src' => 'video.ogg','type' => "video/ogg; codecs='theora, vorbis'")),array('autoplay')); ?>// Output<video autoplay="autoplay"><source src="/files/video.mp4" type="video/mp4"/><source src="/files/video.ogg" type="video/ogg;
codecs='theora, vorbis'"/></video>
HtmlHelper::tag(string $tag, string $text, array $options)Parameters• $tag (string) – The tag name being generated.
• $text (string) – The contents for the tag.
• $options (array) – An array of html attributes.
Returns text wrapped in a specified tag. If no text is specified then only the opening <tag> is returned.:<?php
echo $this->Html->tag('span', 'Hello World.', array('class' => 'welcome'));?>// Output<span class="welcome">Hello World</span>// No text specified.
<?php
echo $this->Html->tag('span', null, array('class' => 'welcome'));?>// Output<span class="welcome">

Note: Text is not escaped by default but you may use $options['escape'] = true to escape your text. Thisreplaces a fourth parameter boolean $escape = false that was available in previous versions.
HtmlHelper::div(string $class, string $text, array $options)Parameters• $class (string) – The class name for the div.
• $text (string) – The content inside the div.
• $options (array) – An array of html attributes.

134

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Used for creating div-wrapped sections of markup. The first parameter specifies a CSS class, and the second isused to supply the text to be wrapped by div tags. If the ‘escape’ key has been set to true in the last parameter,$text will be printed HTML-escaped.
If no text is specified, only an opening div tag is returned.:<?php
echo $this->Html->div('error', 'Please enter your credit card number.');?>// Output<div class="error">Please enter your credit card number.</div>
HtmlHelper::para(string $class, string $text, array $options)Parameters• $class (string) – The class name for the paragraph.
• $text (string) – The content inside the paragraph.
• $options (array) – An array of html attributes.
Returns a text wrapped in a CSS-classed <p> tag. If no text is supplied, only a starting <p> tag is returned.:<?php
echo $this->Html->para(null, 'Hello World.');?>// Output<p>Hello World.</p>
HtmlHelper::script(mixed $url, mixed $options)Parameters• $url (mixed) – Either a string to a single JavaScript file, or an array of strings for multiple
files.
• $options (array) – An array of html attributes.
Include a script file(s), contained either locally or as a remote URL.
By default, script tags are added to the document inline. If you override this by setting $options['inline'] tofalse, the script tags will instead be added to the script block which you can print elsewhere in the document.
If you wish to override which block name is used, you can do so by setting $options['block'].
$options['once'] controls whether or not you want to include this script once per request or more than once.
This defaults to true.
You can use $options to set additional properties to the generated script tag. If an array of script tags is used, theattributes will be applied to all of the generated script tags.
This method of JavaScript file inclusion assumes that the JavaScript file specified resides inside the /app/webroot/js directory:echo $this->Html->script('scripts');
Will output:

More about Views

135CakePHP Cookbook Documentation, Release 2.x<script type="text/javascript" href="/js/scripts.js"></script>
You can link to files with absolute paths as well to link files that are not in app/webroot/js:
echo $this->Html->script('/otherdir/script_file');You can also link to a remote URL:
echo $this->Html->script('https://code.jquery.com/jquery.min.js');
Will output:<script type="text/javascript" href="https://code.jquery.com/jquery.min.js"></script>
The first parameter can be an array to include multiple files.
echo $this->Html->script(array('jquery', 'wysiwyg', 'scripts'));Will output:<script type="text/javascript" href="/js/jquery.js"></script><script type="text/javascript" href="/js/wysiwyg.js"></script><script type="text/javascript" href="/js/scripts.js"></script>
You can append the script tag to a specific block using the block option:echo $this->Html->script('wysiwyg', array('block' => 'scriptBottom'));In your layout you can output all the script tags added to ‘scriptBottom’:echo $this->fetch('scriptBottom');
You can include script files from any loaded plugin using plugin syntax. To include app/Plugin/DebugKit/
webroot/js/toolbar.js you could use the following:echo $this->Html->script('DebugKit.toolbar.js');
If you want to include a script file which shares a name with a loaded plugin you can do the following. For example
if you had a Blog plugin, and also wanted to include app/webroot/js/Blog.plugins.js, you would:
echo $this->Html->script('Blog.plugins.js', array('plugin' => false));Changed in version 2.1: The block option was added. Support for plugin syntax was added.
HtmlHelper::scriptBlock($code, $options = array())
Parameters• $code (string) – The code to go in the script tag.
• $options (array) – An array of html attributes.
Generate a code block containing $code.
Set $options['inline'] to false to have the script
block appear in the script view block. Other options defined will be added as attributes to script
tags. $this->Html->scriptBlock('stuff', array('defer' => true)); will create a script tag withdefer="defer" attribute.

136

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

HtmlHelper::scriptStart($options = array())
Parameters• $options (array) – An array of html attributes to be used when scriptEnd is called.
Begin a buffering code block. This code block will capture all output between scriptStart() andscriptEnd() and create an script tag. Options are the same as scriptBlock()
HtmlHelper::scriptEnd()End a buffering script block, returns the generated script element or null if the script block was opened with
inline = false.
An example of using scriptStart() and scriptEnd() would be:$this->Html->scriptStart(array('inline' => false));echo $this->Js->alert('I am in the javascript');$this->Html->scriptEnd();
HtmlHelper::nestedList(array $list, array $options = array(), array $itemOptions = array(), string $tag = 'ul')Parameters• $list (array) – Set of elements to list.
• $options (array) – Additional HTML attributes of the list (ol/ul) tag or if ul/ol use that astag.
• $itemOptions (array) – Additional HTML attributes of the list item (LI) tag.
• $tag (string) – Type of list tag to use (ol/ul).
Build a nested list (UL/OL) out of an associative array:$list = array('Languages' => array('English' => array('American','Canadian','British',),'Spanish','German',));echo $this->Html->nestedList($list);
Output:// Output (minus the whitespace)<ul><li>Languages<ul><li>English<ul><li>American</li><li>Canadian</li>(continues on next page)

More about Views

137

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)<li>British</li>
</ul>
</li>
<li>Spanish</li><li>German</li>
</ul>
</li>
</ul>
HtmlHelper::tableHeaders(array $names, array $trOptions = null, array $thOptions = null)Parameters• $names (array) – An array of strings to create table headings.
• $trOptions (array) – An array of html attributes for the <tr>
• $thOptions (array) – An array of html attributes for the <th> elements
Creates a row of table header cells to be placed inside of <table> tags.
echo $this->Html->tableHeaders(array('Date', 'Title', 'Active'));Output:<tr><th>Date</th><th>Title</th><th>Active</th></tr>
echo $this->Html->tableHeaders(array('Date','Title','Active'),array('class' => 'status'),array('class' => 'product_table'));
Output:<tr class="status"><th class="product_table">Date</th><th class="product_table">Title</th><th class="product_table">Active</th></tr>
Changed in version 2.2: tableHeaders() now accepts attributes per cell, see below.
As of 2.2 you can set attributes per column, these are used instead of the defaults provided in the $thOptions:echo $this->Html->tableHeaders(array('id',array('Name' => array('class' => 'highlight')),array('Date' => array('class' => 'sortable'))));Output:138

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x<tr><th>id</th><th class="highlight">Name</th><th class="sortable">Date</th></tr>
HtmlHelper::tableCells(array $data, array $oddTrOptions = null, array $evenTrOptions = null, $useCount =false, $continueOddEven = true)
Parameters• $data (array) – A two dimensional array with data for the rows.
• $oddTrOptions (array) – An array of html attributes for the odd <tr>’s.
• $evenTrOptions (array) – An array of html attributes for the even <tr>’s.
• $useCount (boolean) – Adds class “column-$i”.
• $continueOddEven (boolean) – If false, will use a non-static $count variable, so that the
odd/even count is reset to zero just for that call.
Creates table cells, in rows, assigning <tr> attributes differently for odd- and even-numbered rows. Wrap a single
table cell within an array() for specific <td>-attributes.
echo $this->Html->tableCells(array(
array('Jul 7th, 2007', 'Best Brownies', 'Yes'),array('Jun 21st, 2007', 'Smart Cookies', 'Yes'),array('Aug 1st, 2006', 'Anti-Java Cake', 'No'),));
Output:<tr><td>Jul 7th, 2007</td><td>Best Brownies</td><td>Yes</td></tr><tr><td>Jun 21st, 2007</td><td>Smart Cookies</td><td>Yes</td></tr><tr><td>Aug 1st, 2006</td><td>Anti-Java Cake</td><td>No</td></tr>
echo $this->Html->tableCells(array(
array('Jul 7th, 2007',array('Best Brownies',
array('class' => 'highlight')),'Yes'),
array('Jun 21st, 2007', 'Smart Cookies', 'Yes'),array('Aug 1st, 2006','Anti-Java Cake',array('No', array('id' => 'special'))),));Output:More about Views

139CakePHP Cookbook Documentation, Release 2.x

<tr>
<td>
Jul 7th, 2007</td>
<td class="highlight">
Best Brownies
</td>
<td>
Yes
</td>
</tr>
<tr>
<td>
Jun 21st, 2007
</td>
<td>
Smart Cookies
</td>
<td>
Yes
</td>
</tr>
<tr>
<td>
Aug 1st, 2006
</td>
<td>
Anti-Java Cake</td>
<td id="special">
No
</td>
</tr>
echo $this->Html->tableCells(
array(
array('Red', 'Apple'),array('Orange', 'Orange'),array('Yellow', 'Banana'),),array('class' => 'darker'));
Output:<tr class="darker"><td>Red</td><td>Apple</td></tr><tr><td>Orange</td><td>Orange</td></tr><tr class="darker"><td>Yellow</td><td>Banana</td></tr>
HtmlHelper::url(mixed $url = NULL, boolean $full = false)
Parameters• $url (mixed) – A routing array.
140

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x• $full (mixed) – Either a boolean to indicate whether or not the base path should be included
or an array of options for Router::url()
Returns a URL pointing to a combination of controller and action. If $url is empty, it returns the REQUEST_URI,
otherwise it generates the URL for the controller and action combo. If full is true, the full base URL will be
prepended to the result:echo $this->Html->url(array("controller" => "posts","action" => "view",
"bar"
));
// Output/posts/view/bar
Here are a few more usage examples:
URL with named parameters:
echo $this->Html->url(array("controller" => "posts","action" => "view","foo" => "bar"));// Output/posts/view/foo:bar
URL with extension:echo $this->Html->url(array("controller" => "posts","action" => "list","ext" => "rss"));// Output/posts/list.rss
URL (starting with ‘/’) with the full base URL prepended:echo $this->Html->url('/posts', true);// Output
http://somedomain.com/posts
URL with GET params and named anchor:echo $this->Html->url(array("controller" => "posts","action" => "search","?" => array("foo" => "bar"),"#" => "first"));(continues on next page)

More about Views

141CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// Output/posts/search?foo=bar#first
For further information check Router::url57 in the API.
HtmlHelper::useTag(string $tag)
Returns a formatted existent block of $tag:$this->Html->useTag('form','http://example.com',array('method' => 'post', 'class' => 'myform'));
Output:<form action="http://example.com" method="post" class="myform">

Changing the tags output by HtmlHelper
HtmlHelper::loadConfig(mixed $configFile, string $path = null)
The built-in tag sets for HtmlHelper are XHTML compliant, however if you need to generate HTML for HTML5
you will need to create and load a new tags config file containing the tags you’d like to use. To change the tags
used create app/Config/html5_tags.php containing:$config = array('tags' => array('css' => '<link rel="%s" href="%s" %s>','style' => '<style%s>%s</style>','charset' => '<meta charset="%s">','javascriptblock' => '<script%s>%s</script>','javascriptstart' => '<script>','javascriptlink' => '<script src="%s"%s></script>',// ...
));
You can then load this tag set by calling $this->Html->loadConfig('html5_tags');
Creating breadcrumb trails with HtmlHelper
HtmlHelper::getCrumbs(string $separator = '&raquo;', string|array|bool $startText = false)
CakePHP has the built-in ability to automatically create a breadcrumb trail in your app. To set this up, first add
something similar to the following in your layout template:echo $this->Html->getCrumbs(' > ', 'Home');
The $startText option can also accept an array. This gives more control over the generated first link:
57 https://api.cakephp.org/2.x/class-Router.html#_url

142

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

echo $this->Html->getCrumbs(' > ', array('text' => $this->Html->image('home.png'),'url' => array('controller' => 'pages', 'action' => 'display', 'home'),'escape' => false));
Any keys that are not text or url will be passed to link() as the $options parameter.
Changed in version 2.1: The $startText parameter now accepts an array.
HtmlHelper::addCrumb(string $name, string $link = null, mixed $options = null)Now, in your view you’ll want to add the following to start the breadcrumb trails on each of the pages:$this->Html->addCrumb('Users', '/users');$this->Html->addCrumb('Add User', array('controller' => 'users', 'action' => 'add˓→'));
This will add the output of “Users > Add User” in your layout where getCrumbs was added.
You can prepend a crumb with the prepend option:$this->Html->addCrumb('Add User', array('controller' => 'users', 'action' => 'add˓→'));$this->Html->addCrumb('Users', '/users', array('prepend' => true));
This will add the output of “Users > Add User” in your layout where getCrumbs was added.
New in version 2.10: The prepend option was added in 2.10
HtmlHelper::getCrumbList(array $options = array(), mixed $startText)
Parameters• $options (array) – An array of html attributes for the containing <ul> element. Can also
contain the ‘separator’, ‘firstClass’, ‘lastClass’ and ‘escape’ options.
• $startText (string|array) – The text or element that precedes the ul.
Returns breadcrumbs as a (x)html list.
This method uses HtmlHelper::tag() to generate list and its elements. Works similar to getCrumbs(), so it
uses options which every crumb was added with. You can use the $startText parameter to provide the firstbreadcrumb link/text. This is useful when you always want to include a root link. This option works the same as
the $startText option for getCrumbs().
Changed in version 2.1: The $startText parameter was added.
Changed in version 2.3: The ‘separator’, ‘firstClass’ and ‘lastClass’ options were added.
Changed in version 2.5: The ‘escape’ option was added.

More about Views

143

CakePHP Cookbook Documentation, Release 2.x

JsHelperclass JsHelper(View $view, array $settings = array())
Warning: The JsHelper is currently deprecated and completely removed in 3.x. We recommend using regular
JavaScript and directly interacting with JavaScript libraries where possible.
Since the beginning CakePHP’s support for JavaScript has been with Prototype/Scriptaculous. While we still thinkthese are excellent JavaScript libraries, the community has been asking for support for other libraries. Rather thandrop Prototype in favour of another JavaScript library. We created an Adapter based helper, and included 3 of the mostrequested libraries. Prototype/Scriptaculous, Mootools/Mootools-more, and jQuery/jQuery UI. While the API is not
as expansive as the previous AjaxHelper we feel that the adapter based solution allows for a more extensible solution
giving developers the power and flexibility they need to address their specific application needs.
JavaScript Engines form the backbone of the new JsHelper. A JavaScript engine translates an abstract JavaScript
element into concrete JavaScript code specific to the JavaScript library being used. In addition they create an extensible
system for others to use.
Using a specific JavaScript engine
First of all download your preferred JavaScript library and place it in app/webroot/js
Then you must include the library in your page. To include it in all pages, add this line to the <head> section ofapp/View/Layouts/default.ctp:
echo $this->Html->script('jquery'); // Include jQuery library
Replace jquery with the name of your library file (.js will be added to the name).
By default scripts are cached, and you must explicitly print out the cache. To do this at the end of each page, include
this line just before the ending </body> tag:echo $this->Js->writeBuffer(); // Write cached scripts

Warning: You must include the library in your page and print the cache for the helper to function.
JavaScript engine selection is declared when you include the helper in your controller:
public $helpers = array('Js' => array('Jquery'));The above would use the Jquery Engine in the instances of JsHelper in your views. If you do not declare a specificengine, the jQuery engine will be used as the default. As mentioned before, there are three engines implemented in thecore, but we encourage the community to expand the library compatibility.

144

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Using jQuery with other libraries
The jQuery library, and virtually all of its plugins are constrained within the jQuery namespace. As a general rule,“global” objects are stored inside the jQuery namespace as well, so you shouldn’t get a clash between jQuery and anyother library (like Prototype, MooTools, or YUI).
That said, there is one caveat: By default, jQuery uses “$” as a shortcut for “jQuery”
To override the “$” shortcut, use the jQueryObject variable:$this->Js->JqueryEngine->jQueryObject = '$j';echo $this->Html->scriptBlock('var $j = jQuery.noConflict();',
array('inline' => false));// Tell jQuery to go into noconflict mode

Using the JsHelper inside customHelpers
Declare the JsHelper in the $helpers array in your customHelper:
public $helpers = array('Js');

Note: It is not possible to declare a JavaScript engine inside a custom helper. Doing that will have no effect.
If you are willing to use an other JavaScript engine than the default, do the helper setup in your controller as follows:
public $helpers = array('Js' => array('Prototype'),'CustomHelper');

Warning: Be sure to declare the JsHelper and its engine on top of the $helpers array in your controller.
The selected JavaScript engine may disappear (replaced by the default) from the JsHelper object in your helper, if you
miss to do so and you will get code that does not fit your JavaScript library.
Creating a JavaScript Engine
JavaScript engine helpers follow normal helper conventions, with a few additional restrictions. They must havethe Engine suffix. DojoHelper is not good, DojoEngineHelper is correct. Furthermore, they should extend
JsBaseEngineHelper in order to leverage the most of the new API.

More about Views

145

CakePHP Cookbook Documentation, Release 2.x

JavaScript engine usage
The JsHelper provides a few methods, and acts as a facade for the the Engine helper. You should not directly accessthe Engine helper except in rare occasions. Using the facade features of the JsHelper allows you to leverage thebuffering and method chaining features built-in; (method chaining only works in PHP5).
The JsHelper by default buffers almost all script code generated, allowing you to collect scripts throughout the view, elements and layout, and output it in one place. Outputting buffered scripts is done with $this->Js->writeBuffer();
this will return the buffer contents in a script tag. You can disable buffering wholesale with the $bufferScripts property or setting buffer => false in methods taking $options.
Since most methods in JavaScript begin with a selection of elements in the DOM, $this->Js->get() returns a $this,allowing you to chain the methods using the selection. Method chaining allows you to write shorter, more expressivecode:$this->Js->get('#foo')->event('click', $eventCode);
Is an example of method chaining. Method chaining is not possible in PHP4 and the above sample would be written
like:$this->Js->get('#foo');$this->Js->event('click', $eventCode);

Common options
In attempts to simplify development where JavaScript libraries can change, a common set of options is supported by
JsHelper, these common options will be mapped out to the library specific options internally. If you are not planningon switching JavaScript libraries, each library also supports all of its native callbacks and options.
Callback wrapping
By default all callback options are wrapped with the an anonymous function with the correct arguments. You candisable this behavior by supplying the wrapCallbacks = false in your options array.
Working with buffered scripts
One drawback to previous implementation of ‘Ajax’ type features was the scattering of script tags throughout yourdocument, and the inability to buffer scripts added by elements in the layout. The new JsHelper if used correctly avoids
both of those issues. It is recommended that you place $this->Js->writeBuffer() at the bottom of your layout file
above the </body> tag. This will allow all scripts generated in layout elements to be output in one place. It should be
noted that buffered scripts are handled separately from included script files.
JsHelper::writeBuffer($options = array())
Writes all JavaScript generated so far to a code block or caches them to a file and returns a linked script.
Options• inline - Set to true to have scripts output as a script block inline if cache is also true, a script link tag will be
generated. (default true)• cache - Set to true to have scripts cached to a file and linked in (default false)• clear - Set to false to prevent script cache from being cleared (default true)

146

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x• onDomReady - wrap cached scripts in domready event (default true)• safe - if an inline block is generated should it be wrapped in <![CDATA[ . . . ]]> (default true)
Creating a cache file with writeBuffer() requires that webroot/js be world writable and allows a browser to cachegenerated script resources for any page.
JsHelper::buffer($content)
Add $content to the internal script buffer.
JsHelper::getBuffer($clear = true)
Get the contents of the current buffer. Pass in false to not clear the buffer at the same time.
Buffering methods that are not normally buffered
Some methods in the helpers are buffered by default. The engines buffer the following methods by default:• event• sortable• drag• drop• sliderAdditionally you can force any other method in JsHelper to use the buffering. By appending an boolean to the end of
the arguments you can force other methods to go into the buffer. For example the each() method does not normallybuffer:$this->Js->each('alert("whoa!");', true);
The above would force the each() method to use the buffer. Conversely if you want a method that does buffer to notbuffer, you can pass a false in as the last argument:$this->Js->event('click', 'alert("whoa!");', false);
This would force the event function which normally buffers to return its result.
Other Methods
The core JavaScript Engines provide the same feature set across all libraries, there is also a subset of common options
that are translated into library specific options. This is done to provide end developers with as unified an API as
possible. The following list of methods are supported by all the Engines included in the CakePHP core. Whenever you
see separate lists for Options and Event Options both sets of parameters are supplied in the $options array for the
method.
JsHelper::object($data, $options = array())
Serializes $data into JSON. This method is a proxy for json_encode() with a few extra features added via the$options parameter.
Options:• prefix - String prepended to the returned data.
• postfix - String appended to the returned data.
Example Use:

More about Views

147

CakePHP Cookbook Documentation, Release 2.x$json = $this->Js->object($data);
JsHelper::sortable($options = array())
Sortable generates a JavaScript snippet to make a set of elements (usually a list) drag and drop sortable.
The normalized options are:Options• containment - Container for move action• handle - Selector to handle element. Only this element will start sort action.
• revert - Whether or not to use an effect to move sortable into final position.
• opacity - Opacity of the placeholder• distance - Distance a sortable must be dragged before sorting starts.
Event Options• start - Event fired when sorting starts• sort - Event fired during sorting• complete - Event fired when sorting completes.
Other options are supported by each JavaScript library, and you should check the documentation for your
JavaScript library for more detailed information on its options and parameters.
Example Use:
$this->Js->get('#my-list');$this->Js->sortable(array('distance' => 5,'containment' => 'parent','start' => 'onStart','complete' => 'onStop','sort' => 'onSort','wrapCallbacks' => false));
Assuming you were using the jQuery engine, you would get the following code in your generated JavaScript
block$("#myList").sortable({
containment:"parent",
distance:5,
sort:onSort,
start:onStart,
stop:onStop});
JsHelper::request($url, $options = array())
Generate a JavaScript snippet to create an XmlHttpRequest or ‘AJAX’ request.
Event Options• complete - Callback to fire on complete.
• success - Callback to fire on success.

148

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x• before - Callback to fire on request initialization.
• error - Callback to fire on request failure.
Options• method - The method to make the request with defaults to GET in more libraries• async - Whether or not you want an asynchronous request.
• data - Additional data to send.
• update - Dom id to update with the content of the response.
• type - Data type for response. ‘json’ and ‘html’ are supported. Default is html for most libraries.
• evalScripts - Whether or not <script> tags should be eval’ed.
• dataExpression - Should the data key be treated as a callback.
$options['data'] as another JavaScript expression.

Useful for supplying

Example use:
$this->Js->event('click',$this->Js->request(
array('action' => 'foo', 'param1'),
array('async' => true, 'update' => '#element')));
JsHelper::get($selector)
Set the internal ‘selection’ to a CSS selector. The active selection is used in subsequent operations until a new
selection is made:$this->Js->get('#element');
The JsHelper now will reference all other element based methods on the selection of #element. To change
the active selection, call get() again with a new element.
JsHelper::set(mixed $one, mixed $two = null)
Pass variables into JavaScript. Allows you to set variables that will be output when the buffer is fetched with
JsHelper::getBuffer() or JsHelper::writeBuffer(). The JavaScript variable used to output set variables can be controlled with JsHelper::$setVariable.
JsHelper::drag($options = array())
Make an element draggable.
Options• handle - selector to the handle element.
• snapGrid - The pixel grid that movement snaps to, an array(x, y)• container - The element that acts as a bounding box for the draggable element.
Event Options• start - Event fired when the drag starts• drag - Event fired on every step of the drag• stop - Event fired when dragging stops (mouse release)

More about Views

149

CakePHP Cookbook Documentation, Release 2.x

Example use:
$this->Js->get('#element');$this->Js->drag(array('container' => '#content','start' => 'onStart','drag' => 'onDrag','stop' => 'onStop','snapGrid' => array(10, 10),'wrapCallbacks' => false));
If you were using the jQuery engine the following code would be added to the buffer$("#element").draggable({
containment:"#content",
drag:onDrag,grid:[10,10],
start:onStart,
stop:onStop});
JsHelper::drop($options = array())
Make an element accept draggable elements and act as a dropzone for dragged elements.
Options• accept - Selector for elements this droppable will accept.
• hoverclass - Class to add to droppable when a draggable is over.
Event Options• drop - Event fired when an element is dropped into the drop zone.
• hover - Event fired when a drag enters a drop zone.
• leave - Event fired when a drag is removed from a drop zone without being dropped.
Example use:
$this->Js->get('#element');$this->Js->drop(array('accept' => '.items','hover' => 'onHover','leave' => 'onExit','drop' => 'onDrop','wrapCallbacks' => false));
If you were using the jQuery engine the following code would be added to the buffer$("#element").droppable({
accept:".items",
drop:onDrop,
out:onExit,
over:onHover
});

150

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Note: Droppables in Mootools function differently from other libraries. Droppables are implemented as anextension of Drag. So in addition to making a get() selection for the droppable element. You must also provide
a selector rule to the draggable element. Furthermore, Mootools droppables inherit all options from Drag.
JsHelper::slider($options = array())
Create snippet of JavaScript that converts an element into a slider ui widget. See your libraries implementation
for additional usage and features.
Options• handle - The id of the element used in sliding.
• direction - The direction of the slider either ‘vertical’ or ‘horizontal’• min - The min value for the slider.
• max - The max value for the slider.
• step - The number of steps or ticks the slider will have.
• value - The initial offset of the slider.
Events• change - Fired when the slider’s value is updated• complete - Fired when the user stops sliding the handle
Example use:
$this->Js->get('#element');$this->Js->slider(array('complete' => 'onComplete','change' => 'onChange','min' => 0,'max' => 10,'value' => 2,'direction' => 'vertical','wrapCallbacks' => false));
If you were using the jQuery engine the following code would be added to the buffer$("#element").slider({change:onChange,
max:10,
min:0,
orientation:"vertical",stop:onComplete,value:2});
JsHelper::effect($name, $options = array())
Creates a basic effect. By default this method is not buffered and returns its result.
Supported effect names
The following effects are supported by all JsEngines

More about Views

151CakePHP Cookbook Documentation, Release 2.x• show - reveal an element.
• hide - hide an element.
• fadeIn - Fade in an element.
• fadeOut - Fade out an element.
• slideIn - Slide an element in.
• slideOut - Slide an element out.
Options• speed - Speed at which the animation should occur. Accepted values are ‘slow’, ‘fast’. Not all effects use
the speed option.
Example use
If you were using the jQuery engine:$this->Js->get('#element');$result = $this->Js->effect('fadeIn');// $result contains $("#foo").fadeIn();
JsHelper::event($type, $content, $options = array())
Bind an event to the current selection. $type can be any of the normal DOM events or a custom event typeif your library supports them. $content should contain the function body for the callback. Callbacks will bewrapped with function (event) { ... } unless disabled with the $options.
Options• wrap - Whether you want the callback wrapped in an anonymous function. (defaults to true)• stop - Whether you want the event to stop. (defaults to true)
Example use:
$this->Js->get('#some-link');$this->Js->event('click', $this->Js->alert('hey you!'));
If you were using the jQuery library you would get the following JavaScript code:$('#some-link').bind('click', function (event) {alert('hey you!');return false;});
You can remove the return false; by passing setting the stop option to false:$this->Js->get('#some-link');$this->Js->event('click',$this->Js->alert('hey you!'),array('stop' => false));
If you were using the jQuery library you would the following JavaScript code would be added to the buffer. Notethat the default browser event is not cancelled:

152

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x$('#some-link').bind('click', function (event) {alert('hey you!');});JsHelper::domReady($callback)Creates the special ‘DOM ready’ event. JsHelper::writeBuffer() automatically wraps the buffered scripts
in a domReady method.
JsHelper::each($callback)Create a snippet that iterates over the currently selected elements, and inserts $callback.
Example:$this->Js->get('div.message');$this->Js->each('$(this).css({color: "red"});');
Using the jQuery engine would create the following JavaScript:$('div.message').each(function () { $(this).css({color: "red"}); });JsHelper::alert($message)
Create a JavaScript snippet containing an alert() snippet. By default, alert does not buffer, and returns the
script snippet.
$alert = $this->Js->alert('Hey there');JsHelper::confirm($message)
Create a JavaScript snippet containing a confirm() snippet. By default, confirm does not buffer, and returns
the script snippet.
$alert = $this->Js->confirm('Are you sure?');JsHelper::prompt($message, $default)
Create a JavaScript snippet containing a prompt() snippet. By default, prompt does not buffer, and returns the
script snippet.
$prompt = $this->Js->prompt('What is your favorite color?', 'blue');
JsHelper::submit($caption = null, $options = array())
Create a submit input button that enables XmlHttpRequest submitted forms. Options can include both thosefor FormHelper::submit() and JsBaseEngine::request(), JsBaseEngine::event();
Forms submitting with this method, cannot send files. Files do not transfer over XmlHttpRequest and require
an iframe, or other more specialized setups that are beyond the scope of this helper.
Options• url - The URL you wish the XHR request to submit to.
• confirm - Confirm message displayed before sending the request. Using confirm, does not replace any
before callback methods in the generated XmlHttpRequest.
• buffer - Disable the buffering and return a script tag in addition to the link.
• wrapCallbacks - Set to false to disable automatic callback wrapping.
Example use:

More about Views

153

CakePHP Cookbook Documentation, Release 2.x

echo $this->Js->submit('Save', array('update' => '#content'));Will create a submit button with an attached onclick event. The click event will be buffered by default.
echo $this->Js->submit('Save', array('update' => '#content','div' => false,'type' => 'json','async' => false));
Shows how you can combine options that both FormHelper::submit() and JsHelper::request() whenusing submit.
JsHelper::link($title, $url = null, $options = array())
Create an HTML anchor element that has a click event bound to it. Options can include both those for
HtmlHelper::link() and JsHelper::request(), JsHelper::event(), $options is a html attributes array that are appended to the generated anchor element. If an option is not part of the standard attributes or$htmlAttributes it will be passed to JsHelper::request() as an option. If an id is not supplied, a randomly generated one will be created for each link generated.
Options• confirm - Generate a confirm() dialog before sending the event.
• id - use a custom id.
• htmlAttributes - additional non-standard htmlAttributes. Standard attributes are class, id, rel, title,
escape, onblur and onfocus.
• buffer - Disable the buffering and return a script tag in addition to the link.
Example use:echo $this->Js->link('Page 2',array('page' => 2),array('update' => '#content'));
Will create a link pointing to /page:2 and updating #content with the response.
You can use the htmlAttributes option to add in additional custom attributes.
echo $this->Js->link('Page 2', array('page' => 2), array('update' => '#content','htmlAttributes' => array('other' => 'value')));
Outputs the following HTML:<a href="/posts/index/page:2" other="value">Page 2</a>
JsHelper::serializeForm($options = array())
Serialize the form attached to $selector. Pass true for $isForm if the current selection is a form element. Converts
the form or the form element attached to the current selection into a string/json object (depending on the library
implementation) for use with XHR operations.

154

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Options• isForm - is the current selection a form, or an input? (defaults to false)• inline - is the rendered statement going to be used inside another JS statement? (defaults to false)
Setting inline == false allows you to remove the trailing ;. This is useful when you need to serialize a form
element as part of another JavaScript operation, or use the serialize method in an Object literal.
JsHelper::redirect($url)
Redirect the page to $url using window.location.
JsHelper::value($value)
Converts a PHP-native variable of any type to a JSON-equivalent representation. Escapes any string values into
JSON compatible strings. UTF-8 characters will be escaped.
AJAX Pagination
Much like AJAX Pagination in 1.2, you can use the JsHelper to handle the creation of AJAX pagination links insteadof plain HTML links.
Making AJAX Links
Before you can create AJAX links you must include the JavaScript library that matches the adapter you are usingwith JsHelper. By default the JsHelper uses jQuery. So in your layout include jQuery (or whichever library you
are using). Also make sure to include RequestHandlerComponent in your components. Add the following to yourcontroller:
public $components = array('RequestHandler');
public $helpers = array('Js');
Next link in the JavaScript library you want to use. For this example we’ll be using jQuery:echo $this->Html->script('jquery');
Similar to 1.2 you need to tell the PaginatorHelper that you want to make JavaScript enhanced links instead of plain
HTML ones. To do so, call the options() at the top of your view:$this->Paginator->options(array('update' => '#content','evalScripts' => true));
The PaginatorHelper now knows to make JavaScript enhanced links, and that those links should update the#content element. Of course this element must exist, and often times you want to wrap $content_for_layout
with a div matching the id used for the update option. You also should set evalScripts to true if you are using the
Mootools or Prototype adapters, without evalScripts these libraries will not be able to chain requests together. The
indicator option is not supported by JsHelper and will be ignored.
You then create all the links as needed for your pagination features. Since the JsHelper automatically buffers all
generated script content to reduce the number of <script> tags in your source code you must write the buffer out. At
the bottom of your view file. Be sure to include:
echo $this->Js->writeBuffer();More about Views

155CakePHP Cookbook Documentation, Release 2.x

If you omit this you will not be able to chain AJAX pagination links. When you write the buffer, it is also cleared, so
you don’t have worry about the same JavaScript being output twice.
Adding effects and transitions
Since indicator is no longer supported, you must add any indicator effects yourself:<!DOCTYPE html><html><head><?php echo $this->Html->script('jquery'); ?>//more stuff here.
</head><body><div id="content"><?php echo $this->fetch('content'); ?></div><?php
echo $this->Html->image('indicator.gif',array('id' => 'busy-indicator'));?></body></html>Remember to place the indicator.gif file inside app/webroot/img folder. You may see a situation where the indicator.gif
displays immediately upon the page load. You need to put in this CSS #busy-indicator { display:none; } inyour main CSS file.
With the above layout, we’ve included an indicator image file, that will display a busy indicator animation that we willshow and hide with the JsHelper. To do that we need to update our options() function:$this->Paginator->options(array('update' => '#content','evalScripts' => true,'before' => $this->Js->get('#busy-indicator')->effect('fadeIn',
array('buffer' => false)),'complete' => $this->Js->get('#busy-indicator')->effect('fadeOut',array('buffer' => false)),));
This will show/hide the busy-indicator element before and after the #content div is updated. Although indicator
has been removed, the new features offered by JsHelper allow for more control and more complex effects to be created.

156

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

NumberHelperclass NumberHelper(View $view, array $settings = array())
The NumberHelper contains convenient methods that enable display numbers in common formats in your views. Thesemethods include ways to format currency, percentages, data sizes, format numbers to specific precisions and also togive you more flexibility with formatting numbers.
Changed in version 2.1: NumberHelper have been refactored into CakeNumber class to allow easier use outside of theView layer. Within a view, these methods are accessible via the NumberHelper class and you can call it as you would
call a normal helper method: $this->Number->method($args);.
All of these functions return the formatted number; They do not automatically echo the output into the view.
NumberHelper::currency(float $number, string $currency = 'USD', array $options = array())
Parameters• $number (float) – The value to covert.
• $currency (string) – The known currency format to use.
• $options (array) – Options, see below.
This method is used to display a number in common currency formats (EUR,GBP,USD). Usage in a view looks
like:// called as NumberHelper
echo $this->Number->currency($number, $currency);// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::currency($number, $currency);
The first parameter, $number, should be a floating point number that represents the amount of money you are
expressing. The second parameter is used to choose a predefined currency formatting scheme:$currency

1234.56, formatted by currency type

EUR
GBP
USD

€1.234,56£1,234.56$1,234.56

The third parameter is an array of options for further defining the output. The following options are available:

More about Views

157

CakePHP Cookbook Documentation, Release 2.x

OptionDescription

beforeafter

The currency symbol to place before whole numbers ie. ‘$’
The currency symbol to place after decimal numbers ie. ‘c’. Set to boolean false to use nodecimal symbol. eg. 0.35 => $0.35.
The text to use for zero values, can be a string or a number. ie. 0, ‘Free!’
Number of decimal places to use. ie. 2
Thousands separator ie. ‘,’
Decimal separator symbol ie. ‘.’
Symbol for negative numbers. If equal to ‘()’, the number will be wrapped with ( and )Should the output be htmlentity escaped? Defaults to true
String to use for whole numbers ie. ‘ dollars’

zeroplacesthousands
decimals
negative
escapewholeSymbol
wholePosition
fractionSymbol
fractionPosition
fractionExponent

Either ‘before’ or ‘after’ to place the whole symbol
String to use for fraction numbers ie. ‘ cents’
Either ‘before’ or ‘after’ to place the fraction symbol
Fraction exponent of this specific currency. Defaults to 2.

If a non-recognized $currency value is supplied, it is prepended to a USD formatted number. For example:// called as NumberHelper
echo $this->Number->currency('1234.56', 'FOO');// Outputs
FOO 1,234.56// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::currency('1234.56', 'FOO');
Changed in version 2.4: The fractionExponent option was added.
NumberHelper::defaultCurrency(string $currency)
Parameters• $currency (string) – Set a known currency for CakeNumber::currency().
Setter/getter for default currency.
This removes the need always passing the currency to
CakeNumber::currency() and change all currency outputs by setting other default.
New in version 2.3: This method was added in 2.3
NumberHelper::addFormat(string $formatName, array $options)Parameters• $formatName (string) – The format name to be used in the future• $options (array) – The array of options for this format. Uses the same $options keys as
CakeNumber::currency().
Add a currency format to the Number helper. Makes reusing currency formats easier:

158

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x// called as NumberHelper$this->Number->addFormat('BRL', array('before' => 'R$', 'thousands' => '.',
˓→'decimals' => ','));// called as CakeNumber
App::uses('CakeNumber', 'Utility');
CakeNumber::addFormat('BRL', array('before' => 'R$', 'thousands' => '.', 'decimals'␣
˓→=> ','));
You can now use BRL as a short form when formatting currency amounts:// called as NumberHelper
echo $this->Number->currency($value, 'BRL');// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::currency($value, 'BRL');
Added formats are merged with the following defaults:
array('wholeSymbol'=> '','wholePosition'=> 'before','fractionSymbol'=> false,'fractionPosition' => 'after','zero'=> 0,'places'=> 2,'thousands'=> ',','decimals'=> '.','negative'=> '()','escape'=> true,'fractionExponent' => 2)
NumberHelper::precision(mixed $number, int $precision = 3)
Parameters• $number (float) – The value to covert• $precision (integer) – The number of decimal places to display
This method displays a number with the specified amount of precision (decimal places). It will round in order tomaintain the level of precision defined.
// called as NumberHelper
echo $this->Number->precision(456.91873645, 2);// Outputs
456.92// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::precision(456.91873645, 2);More about Views

159

CakePHP Cookbook Documentation, Release 2.x

NumberHelper::toPercentage(mixed $number, int $precision = 2, array $options = array())
Parameters• $number (float) – The value to covert.
• $precision (integer) – The number of decimal places to display.
• $options (array) – Options, see below.
OptionDescription

multiply

Boolean to indicate whether the value has to be multiplied by 100. Useful for decimal percentages.

Like precision(), this method formats a number according to the supplied precision (where numbers are rounded
to meet the given precision). This method also expresses the number as a percentage and prepends the output
with a percent sign.
// Called as NumberHelper. Output: 45.69%
echo $this->Number->toPercentage(45.691873645);// Called as CakeNumber. Output: 45.69%
App::uses('CakeNumber', 'Utility');echo CakeNumber::toPercentage(45.691873645);// Called with multiply. Output: 45.69%
echo CakeNumber::toPercentage(0.45691, 2, array('multiply' => true));
New in version 2.4: The $options argument with the multiply option was added.
NumberHelper::fromReadableSize(string $size, $default)
Parameters• $size (string) – The formatted human readable value.
This method unformats a number from a human readable byte size to an integer number of bytes.
New in version 2.3: This method was added in 2.3
NumberHelper::toReadableSize(string $dataSize)Parameters• $dataSize (string) – The number of bytes to make readable.
This method formats data sizes in human readable forms. It provides a shortcut way to convert bytes to KB, MB,
GB, and TB. The size is displayed with a two-digit precision level, according to the size of data supplied (i.e.
higher sizes are expressed in larger terms):// called as NumberHelper
echo $this->Number->toReadableSize(0); // 0 Bytes
echo $this->Number->toReadableSize(1024); // 1 KB
echo $this->Number->toReadableSize(1321205.76); // 1.26 MB
echo $this->Number->toReadableSize(5368709120); // 5.00 GB(continues on next page)

160

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::toReadableSize(0); // 0 Bytes
echo CakeNumber::toReadableSize(1024); // 1 KB
echo CakeNumber::toReadableSize(1321205.76); // 1.26 MB
echo CakeNumber::toReadableSize(5368709120); // 5.00 GB
NumberHelper::format(mixed $number, mixed $options=false)
This method gives you much more control over the formatting of numbers for use in your views (and is used as
the main method by most of the other NumberHelper methods). Using this method might looks like:// called as NumberHelper$this->Number->format($number, $options);// called as CakeNumber
CakeNumber::format($number, $options);
The $number parameter is the number that you are planning on formatting for output. With no $options supplied,the number 1236.334 would output as 1,236. Note that the default precision is zero decimal places.
The $options parameter is where the real magic for this method resides.
• If you pass an integer then this becomes the amount of precision or places for the function.
• If you pass an associated array, you can use the following keys:– places (integer): the amount of desired precision– before (string): to be put before the outputted number– escape (boolean): if you want the value in before to be escaped– decimals (string): used to delimit the decimal places in a number– thousands (string): used to mark off thousand, millions, . . . places
Example:// called as NumberHelper
echo $this->Number->format('123456.7890', array('places' => 2,'before' => '¥ ','escape' => false,'decimals' => '.',
'thousands' => ','));// output '¥ 123,456.79'// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::format('123456.7890', array('places' => 2,'before' => '¥ ','escape' => false,'decimals' => '.',
'thousands' => ','(continues on next page)

More about Views

161CakePHP Cookbook Documentation, Release 2.x(continued from previous page)));// output '¥ 123,456.79'
NumberHelper::formatDelta(mixed $number, mixed $options=array())
This method displays differences in value as a signed number:// called as NumberHelper$this->Number->formatDelta($number, $options);// called as CakeNumber
CakeNumber::formatDelta($number, $options);
The $number parameter is the number that you are planning on formatting for output. With no $options supplied,the number 1236.334 would output as 1,236. Note that the default precision is zero decimal places.
The $options parameter takes the same keys as CakeNumber::format() itself:• places (integer): the amount of desired precision• before (string): to be put before the outputted number• after (string): to be put after the outputted number• decimals (string): used to delimit the decimal places in a number• thousands (string): used to mark off thousand, millions, . . . places
Example:// called as NumberHelper
echo $this->Number->formatDelta('123456.7890', array('places' => 2,'decimals' => '.',
'thousands' => ','));// output '+123,456.79'// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::formatDelta('123456.7890', array('places' => 2,'decimals' => '.',
'thousands' => ','));// output '+123,456.79'
New in version 2.3: This method was added in 2.3
Warning: Since 2.4 the symbols are now UTF-8. Please see the migration guide for details if you run a non-UTF-8
app.

162

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

PaginatorHelperclass PaginatorHelper(View $view, array $settings = array())
The Pagination helper is used to output pagination controls such as page numbers and next/previous links. It works intandem with PaginatorComponent.
See also Pagination for information on how to create paginated datasets and do paginated queries.
Creating sort links
PaginatorHelper::sort($key, $title = null, $options = array())
Parameters• $key (string) – The name of the key that the recordset should be sorted.
• $title (string) – Title for the link. If $title is null $key will be used for the title and will
be generated by inflection.
• $options (array) – Options for sorting link.
Generates a sorting link. Sets named or querystring parameters for the sort and direction. Links will default to sortingby asc. After the first click, links generated with sort() will handle direction switching automatically. Link sorting
default by ‘asc’. If the resultset is sorted ‘asc’ by the specified key the returned link will sort by ‘desc’.
Accepted keys for $options:• escape Whether you want the contents HTML entity encoded, defaults to true.
• model The model to use, defaults to PaginatorHelper::defaultModel().
• direction The default direction to use when this link isn’t active.
• lock Lock direction. Will only use the default direction then, defaults to false.
New in version 2.5: You can now set the lock option to true in order to lock the sorting direction into the specified
direction.
Assuming you are paginating some posts, and are on page one:echo $this->Paginator->sort('user_id');
Output:<a href="/posts/index/page:1/sort:user_id/direction:asc/">User Id</a>
You can use the title parameter to create custom text for your link:echo $this->Paginator->sort('user_id', 'User account');
Output:<a href="/posts/index/page:1/sort:user_id/direction:asc/">User account</a>
If you are using HTML like images in your links remember to set escaping off:

More about Views

163

CakePHP Cookbook Documentation, Release 2.x

echo $this->Paginator->sort('user_id','<em>User account</em>',array('escape' => false));
Output:<a href="/posts/index/page:1/sort:user_id/direction:asc/"><em>User account</em></a>
The direction option can be used to set the default direction for a link. Once a link is active, it will automatically switch
directions like normal:
echo $this->Paginator->sort('user_id', null, array('direction' => 'desc'));Output:<a href="/posts/index/page:1/sort:user_id/direction:desc/">User Id</a>
The lock option can be used to lock sorting into the specified direction:echo $this->Paginator->sort('user_id', null, array('direction' => 'asc', 'lock' =>␣
˓→true));PaginatorHelper::sortDir(string $model = null, mixed $options = array())
Gets the current direction the recordset is sorted.
PaginatorHelper::sortKey(string $model = null, mixed $options = array())
Gets the current key by which the recordset is sorted.
Creating page number links
PaginatorHelper::numbers($options = array())
Returns a set of numbers for the paged result set. Uses a modulus to decide how many numbers to show on each sideof the current page By default 8 links on either side of the current page will be created if those pages exist. Links will
not be generated for pages that do not exist. The current page is also not a link.
Supported options are:• before Content to be inserted before the numbers.
• after Content to be inserted after the numbers.
• model Model to create numbers for, defaults to PaginatorHelper::defaultModel().
• modulus how many numbers to include on either side of the current page, defaults to 8.
• separator Separator content defaults to `` | ``• tag The tag to wrap links in, defaults to ‘span’.
• first Whether you want first links generated, set to an integer to define the number of ‘first’ links to generate.
Defaults to false. If a string is set a link to the first page will be generated with the value as the title:

164

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

echo $this->Paginator->numbers(array('first' => 'First page'));• last Whether you want last links generated, set to an integer to define the number of ‘last’ links to generate.
Defaults to false. Follows the same logic as the first option. There is a last()` method to be used separately
as well if you wish.
• ellipsis Ellipsis content, defaults to ‘. . . ’• class The class name used on the wrapping tag.
• currentClass The class name to use on the current/active link. Defaults to current.
• currentTag Tag to use for current page number, defaults to null. This allows you to generate for example Twitter
Bootstrap like links with the current page number wrapped in extra ‘a’ or ‘span’ tag.
While this method allows a lot of customization for its output. It is also ok to just call the method without any params.
echo $this->Paginator->numbers();
Using the first and last options you can create links to the beginning and end of the page set. The following would
create a set of page links that include links to the first 2 and last 2 pages in the paged results:
echo $this->Paginator->numbers(array('first' => 2, 'last' => 2));
New in version 2.1: The currentClass option was added in 2.1.
New in version 2.3: The currentTag option was added in 2.3.
Creating jump links
In addition to generating links that go directly to specific page numbers, you’ll often want links that go to the previous
and next links, first and last pages in the paged data set.
PaginatorHelper::prev($title = '<< Previous', $options = array(), $disabledTitle = null, $disabledOptions =array())
Parameters• $title (string) – Title for the link.
• $options (mixed) – Options for pagination link.
• $disabledTitle (string) – Title when the link is disabled, as when you’re already on thefirst page, no previous page to go.
• $disabledOptions (mixed) – Options for the disabled pagination link.
Generates a link to the previous page in a set of paged records.
$options and $disabledOptions supports the following keys:• tag The tag wrapping tag you want to use, defaults to ‘span’. Set this to false to disable this option.
• escape Whether you want the contents HTML entity encoded, defaults to true.
• model The model to use, defaults to PaginatorHelper::defaultModel().
• disabledTag Tag to use instead of A tag when there is no previous page
A simple example would be:More about Views

165

CakePHP Cookbook Documentation, Release 2.x

echo $this->Paginator->prev('<< ' . __('previous'),array(),
null,
array('class' => 'prev disabled'));
If you were currently on the second page of posts, you would get the following:<span class="prev"><a rel="prev" href="/posts/index/page:1/sort:title/order:desc">&lt;&lt; previous</a></span>If there were no previous pages you would get:<span class="prev disabled">&lt;&lt; previous</span>
You can change the wrapping tag using the tag option:echo $this->Paginator->prev(__('previous'), array('tag' => 'li'));
Output:<li class="prev"><a rel="prev" href="/posts/index/page:1/sort:title/order:desc">
previous</a></li>
You can also disable the wrapping tag:echo $this->Paginator->prev(__('previous'), array('tag' => false));
Output:<a class="prev" rel="prev"
href="/posts/index/page:1/sort:title/order:desc">
previous</a>Changed in version 2.3: For methods: PaginatorHelper::prev() and PaginatorHelper::next() it is now possible to set the tag option to false to disable the wrapper. New options disabledTag has been added.
If you leave the $disabledOptions empty the $options parameter will be used. This can save some additional
typing if both sets of options are the same.
PaginatorHelper::next($title = 'Next >>', $options = array(), $disabledTitle = null, $disabledOptions =array())
This method is identical to prev() with a few exceptions. It creates links pointing to the next page instead of
the previous one. It also uses next as the rel attribute value instead of prev
PaginatorHelper::first($first = '<< first', $options = array())
Returns a first or set of numbers for the first pages. If a string is given, then only a link to the first page with theprovided text will be created:
166

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

echo $this->Paginator->first('< first');The above creates a single link for the first page. Will output nothing if you are on the first page. You can also
use an integer to indicate how many first paging links you want generated:echo $this->Paginator->first(3);The above will create links for the first 3 pages, once you get to the third or greater page. Prior to that nothing
will be output.
The options parameter accepts the following:• tag The tag wrapping tag you want to use, defaults to ‘span’• after Content to insert after the link/tag• model The model to use defaults to PaginatorHelper::defaultModel()• separator Content between the generated links, defaults to ‘ | ‘• ellipsis Content for ellipsis, defaults to ‘. . . ’
PaginatorHelper::last($last = 'last >>', $options = array())
This method works very much like the first() method. It has a few differences though. It will not generate
any links if you are on the last page for a string values of $last. For an integer value of $last no links will be
generated once the user is inside the range of last pages.
PaginatorHelper::current(string $model = null)Gets the current page of the recordset for the given model:// Our URL is: http://example.com/comments/view/page:3
echo $this->Paginator->current('Comment');// Output is 3
PaginatorHelper::hasNext(string $model = null)
Returns true if the given result set is not at the last page.
PaginatorHelper::hasPrev(string $model = null)
Returns true if the given result set is not at the first page.
PaginatorHelper::hasPage(string $model = null, integer $page = 1)
Returns true if the given result set has the page number given by $page.
Creating a page counter
PaginatorHelper::counter($options = array())
Returns a counter string for the paged result set. Using a provided format string and a number of options you can create
localized and application specific indicators of where a user is in the paged data set.
There are a number of options for counter(). The supported ones are:• format Format of the counter. Supported formats are ‘range’, ‘pages’ and custom. Defaults to pages which
would output like ‘1 of 10’. In the custom mode the supplied string is parsed and tokens are replaced with actual
values. The available tokens are:– {:page} - the current page displayed.
– {:pages} - total number of pages.
More about Views

167

CakePHP Cookbook Documentation, Release 2.x– {:current} - current number of records being shown.
– {:count} - the total number of records in the result set.
– {:start} - number of the first record being displayed.
– {:end} - number of the last record being displayed.
– {:model} - The pluralized human form of the model name. If your model was ‘RecipePage’, {:model}
would be ‘recipe pages’. This option was added in 2.0.
You could also supply only a string to the counter method using the tokens available. For example:
echo $this->Paginator->counter('Page {:page} of {:pages}, showing {:current} records out of{:count} total, starting on record {:start}, ending on {:end}');
Setting ‘format’ to range would output like ‘1 - 3 of 13’:echo $this->Paginator->counter(array('format' => 'range'));• separator The separator between the actual page and the number of pages. Defaults to ‘ of ‘. This is used inconjunction with ‘format’ = ‘pages’ which is ‘format’ default value:echo $this->Paginator->counter(array('separator' => ' of a total of '));• model The name of the model being paginated, defaults to PaginatorHelper::defaultModel(). This isused in conjunction with the custom string on ‘format’ option.
Modifying the options PaginatorHelper uses
PaginatorHelper::options($options = array())
Parameters• $options (mixed) – Default options for pagination links. If a string is supplied - it is usedas the DOM id element to update.
Sets all the options for the Paginator Helper. Supported options are:• url The URL of the paginating action. ‘url’ has a few sub options as well:– sort The key that the records are sorted by.
– direction The direction of the sorting. Defaults to ‘ASC’.
– page The page number to display.
The above mentioned options can be used to force particular pages/directions. You can also append additional
URL content into all URLs generated in the helper:$this->Paginator->options(array('url' => array('sort' => 'email', 'direction' => 'desc', 'page' => 6,(continues on next page)

168

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'lang' => 'en')));
The above adds the en route parameter to all links the helper will generate. It will also create links with specificsort, direction and page values. By default PaginatorHelper will merge in all of the current pass and named
parameters. So you don’t have to do that in each view file.
• escape Defines if the title field for links should be HTML escaped. Defaults to true.
• update The CSS selector of the element to update with the results of AJAX pagination calls. If not specified,regular links will be created:$this->Paginator->options(array('update' => '#content'));This is useful when doing AJAX Pagination. Keep in mind that the value of update can be any valid CSS selector,
but most often is simpler to use an id selector.
• model The name of the model being paginated, defaults to PaginatorHelper::defaultModel().
Using GET parameters for pagination
Normally Pagination in CakePHP uses Named Parameters. There are times you want to use GET parameters instead.
While the main configuration option for this feature is in PaginatorComponent, you have some additional control inthe view. You can use options() to indicate that you want other named parameters to be converted:$this->Paginator->options(array('convertKeys' => array('your', 'keys', 'here')));Configuring the PaginatorHelper to use a JavaScript helper
By default the PaginatorHelper uses JsHelper to do AJAX features. However, if you don’t want that and want touse a custom helper for AJAX links, you can do so by changing the $helpers array in your controller. After running
paginate() do the following:// In your controller action.
$this->set('posts', $this->paginate());$this->helpers['Paginator'] = array('ajax' => 'CustomJs');
Will change the PaginatorHelper to use the CustomJs for AJAX operations. You could also set the ‘ajax’ key to be
any helper, as long as that class implements a link() method that behaves like HtmlHelper::link()

More about Views

169CakePHP Cookbook Documentation, Release 2.x

Pagination in Views
It’s up to you to decide how to show records to the user, but most often this will be done inside HTML tables. The
examples below assume a tabular layout, but the PaginatorHelper available in views doesn’t always need to be restricted
as such.
See the details on PaginatorHelper58 in the API. As mentioned, the PaginatorHelper also offers sorting features which
can be easily integrated into your table column headers:// app/View/Posts/index.ctp<table><tr><th><?php echo $this->Paginator->sort('id', 'ID'); ?></th><th><?php echo $this->Paginator->sort('title', 'Title'); ?></th></tr><?php foreach ($data as $recipe): ?><tr><td><?php echo $recipe['Recipe']['id']; ?> </td><td><?php echo h($recipe['Recipe']['title']); ?> </td></tr><?php endforeach; ?></table>The links output from the sort() method of the PaginatorHelper allow users to click on table headers to toggle thesorting of the data by a given field.
It is also possible to sort a column based on associations:<table><tr><th><?php echo $this->Paginator->sort('title', 'Title'); ?></th><th><?php echo $this->Paginator->sort('Author.name', 'Author'); ?></th></tr><?php foreach ($data as $recipe): ?><tr><td><?php echo h($recipe['Recipe']['title']); ?> </td><td><?php echo h($recipe['Author']['name']); ?> </td></tr><?php endforeach; ?></table>The final ingredient to pagination display in views is the addition of page navigation, also supplied by the PaginationHelper:// Shows the page numbers
echo $this->Paginator->numbers();// Shows the next and previous links
echo $this->Paginator->prev('« Previous',
null,
null,
array('class' => 'disabled')(continues on next page)
58 https://api.cakephp.org/2.x/class-PaginatorHelper.html

170

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page));echo $this->Paginator->next('Next »',
null,
null,
array('class' => 'disabled'));// prints X of Y, where X is current page and Y is number of pages
echo $this->Paginator->counter();
The wording output by the counter() method can also be customized using special markers:
echo $this->Paginator->counter(array('format' => 'Page {:page} of {:pages}, showing {:current} records out of{:count} total, starting on record {:start}, ending on {:end}'));

Other Methods
PaginatorHelper::link($title, $url = array(), $options = array())
Parameters• $title (string) – Title for the link.
• $url (mixed) – Url for the action. See Router::url()• $options (array) – Options for the link. See options() for list of keys.
Accepted keys for $options:• update The Id of the DOM element you wish to update. Creates AJAX enabled links.
• escape Whether you want the contents HTML entity encoded, defaults to true.
• model The model to use, defaults to PaginatorHelper::defaultModel().
Creates a regular or AJAX link with pagination parameters:
echo $this->Paginator->link('Sort by title on page 5',
array('sort' => 'title', 'page' => 5, 'direction' => 'desc'));Ifcreated
in
the
view
for
/posts/index‘/posts/index/page:5/sort:title/direction:desc’Wouldcreatealinkpointing

at

PaginatorHelper::url($options = array(), $asArray = false, $model = null)Parameters• $options (array) – Pagination/URL options array. As used on options() or link()
method.
• $asArray (boolean) – Return the URL as an array, or a URI string. Defaults to false.
• $model (string) – Which model to paginate on
By default returns a full pagination URL string for use in non-standard contexts (i.e. JavaScript).

More about Views

171CakePHP Cookbook Documentation, Release 2.x

echo $this->Paginator->url(array('sort' => 'title'), true);
PaginatorHelper::defaultModel()Gets the default model of the paged sets or null if pagination is not initialized.
PaginatorHelper::params(string $model = null)Gets the current paging parameters from the resultset for the given model:
debug($this->Paginator->params());
/*
Array
(
[page] => 2
[current] => 2
[count] => 43[prevPage] => 1
[nextPage] => 3
[pageCount] => 3
[order] =>[limit] => 20[options] => Array([page] => 2
[conditions] => Array())[paramType] => named)*/
PaginatorHelper::param(string $key, string $model = null)Gets the specific paging parameter from the resultset for the given model:
debug($this->Paginator->param('count'));/*(int)43*/
New in version 2.4: The param() method was added in 2.4.
PaginatorHelper::meta(array $options = array())
Outputs the meta-links for a paginated result set:echo $this->Paginator->meta(); // Example output for page 5/*<link href="/?page=4" rel="prev" /><link href="/?page=6" rel="next" />*/You can also append the output of the meta function to the named block:$this->Paginator->meta(array('block' => true));
If true is passed, the “meta” block is used.

172

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

New in version 2.6: The meta() method was added in 2.6.
RssHelperclass RssHelper(View $view, array $settings = array())
The RSS helper makes generating XML for RSS feeds easy.
Creating an RSS feed with the RssHelperThis example assumes you have a Posts Controller and Post Model already created and want to make an alternative
view for RSS.
Creating an xml/rss version of posts/index is a snap with CakePHP. After a few simple steps you can simply append thedesired extension .rss to posts/index making your URL posts/index.rss. Before we jump too far ahead trying
to get our webservice up and running we need to do a few things. First parseExtensions needs to be activated, this isdone in app/Config/routes.php:
Router::parseExtensions('rss');
In the call above we’ve activated the .rss extension. When using Router::parseExtensions() you can pass as manyarguments or extensions as you want. This will activate each extension/content-type for use in your application. Now
when the address posts/index.rss is requested you will get an xml version of your posts/index. However, first
we need to edit the controller to add in the rss-specific code.
Controller Code
It is a good idea to add RequestHandler to your PostsController’s $components array. This will allow a lot of automagic
to occur:public $components = array('RequestHandler');
Our view will also use the TextHelper for formatting, so that should be added to the controller as well:
public $helpers = array('Text');
Before we can make an RSS version of our posts/index we need to get a few things in order. It may be tempting to
put the channel metadata in the controller action and pass it to your view using the Controller::set() method
but this is inappropriate. That information can also go in the view. That will come later though, for now if you
have a different set of logic for the data used to make the RSS feed and the data for the HTML view you can use theRequestHandler::isRss() method, otherwise your controller can stay the same:// Modify the Posts Controller action that corresponds to// the action which deliver the rss feed, which is the// index action in our example
public function index() {if ($this->RequestHandler->isRss() ) {$posts = $this->Post->find('all',
array('limit' => 20, 'order' => 'Post.created DESC'));(continues on next page)

More about Views

173

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

return $this->set(compact('posts'));}// this is not an Rss request, so deliver// data used by website's interface$this->paginate['Post'] = array('order' => 'Post.created DESC','limit' => 10);$posts = $this->paginate();$this->set(compact('posts'));}With all the View variables set we need to create an rss layout.
LayoutAn Rss layout is very simple, put the following contents in app/View/Layouts/rss/default.ctp:
if (!isset($documentData)) {$documentData = array();}if (!isset($channelData)) {$channelData = array();}if (!isset($channelData['title'])) {$channelData['title'] = $this->fetch('title');}$channel = $this->Rss->channel(array(), $channelData, $this->fetch('content'));echo $this->Rss->document($documentData, $channel);It doesn’t look like much but thanks to the power in the RssHelper it’s doing a lot of lifting for us. We haven’t set$documentData or $channelData in the controller, however in CakePHP your views can pass variables back to thelayout. Which is where our $channelData array will come from setting all of the meta data for our feed.
Next up is view file for my posts/index. Much like the layout file we created, we need to create a View/Posts/rss/
directory and create a new index.ctp inside that folder. The contents of the file are below.
View
Our view, located at app/View/Posts/rss/index.ctp, begins by setting the $documentData and $channelData
variables for the layout, these contain all the metadata for our RSS feed. This is done by using the View::set()method which is analogous to the Controller::set() method. Here though we are passing the channel’s metadataback to the layout:$this->set('channelData', array('title' => __("Most Recent Posts"),'link' => $this->Html->url('/', true),'description' => __("Most recent posts."),(continues on next page)

174

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'language' => 'en-us'));
The second part of the view generates the elements for the actual records of the feed. This is accomplished by looping
through the data that has been passed to the view ($items) and using the RssHelper::item() method. The othermethod you can use, RssHelper::items() which takes a callback and an array of items for the feed. (The method I
have seen used for the callback has always been called transformRss(). There is one downfall to this method, whichis that you cannot use any of the other helper classes to prepare your data inside the callback method because the scope
inside the method does not include anything that is not passed inside, thus not giving access to the TimeHelper or any
other helper that you may need. The RssHelper::item() transforms the associative array into an element for eachkey value pair.
Note: You will need to modify the $postLink variable as appropriate to your application.
foreach ($posts as $post) {$postTime = strtotime($post['Post']['created']);$postLink = array('controller' => 'posts','action' => 'view','year' => date('Y', $postTime),'month' => date('m', $postTime),'day' => date('d', $postTime),$post['Post']['slug']);// Remove & escape any HTML to make sure the feed content will validate.
$bodyText = h(strip_tags($post['Post']['body']));$bodyText = $this->Text->truncate($bodyText, 400, array('ending' => '...','exact' => true,'html'=> true,));echo$this->Rss->item(array(), array('title' => $post['Post']['title'],'link' => $postLink,'guid' => array('url' => $postLink, 'isPermaLink' => 'true'),'description' => $bodyText,'pubDate' => $post['Post']['created']));}
You can see above that we can use the loop to prepare the data to be transformed into XML elements. It is importantto filter out any non-plain text characters out of the description, especially if you are using a rich text editor for the
body of your blog. In the code above we used strip_tags() and h() to remove/escape any XML special charactersfrom the content, as they could cause validation errors. Once we have set up the data for the feed, we can then use the
RssHelper::item() method to create the XML in RSS format. Once you have all this setup, you can test your RSS
feed by going to your site /posts/index.rss and you will see your new feed. It is always important that you validate
your RSS feed before making it live. This can be done by visiting sites that validate the XML such as Feed Validator
or the w3c site at https://validator.w3.org/feed/.

More about Views

175

CakePHP Cookbook Documentation, Release 2.x

Note: You may need to set the value of ‘debug’ in your core configuration to 1 or to 0 to get a valid feed, because of thevarious debug information added automagically under higher debug settings that break XML syntax or feed validation
rules.

Rss Helper API
property RssHelper::$action
Current action
property RssHelper::$base
Base URL
property RssHelper::$data
POSTed model data
property RssHelper::$field
Name of the current field
property RssHelper::$helpers
Helpers used by the RSS Helper
property RssHelper::$here
URL to current action
property RssHelper::$model
Name of current model
property RssHelper::$params
Parameter array
property RssHelper::$version
Default spec version of generated RSS.
RssHelper::channel(array $attrib = array (), array $elements = array (), mixed $content = null)
Return typestringReturns an RSS <channel /> element.
RssHelper::document(array $attrib = array (), string $content = null)
Return typestringReturns an RSS document wrapped in <rss /> tags.
RssHelper::elem(string $name, array $attrib = array (), mixed $content = null, boolean $endTag = true)
Return typestringGenerates an XML element.

176

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

RssHelper::item(array $att = array (), array $elements = array ())
Return typestringConverts an array into an <item /> element and its contents.
RssHelper::items(array $items, mixed $callback = null)
Return typestringTransforms an array of data using an optional callback, and maps it to a set of <item /> tags.
RssHelper::time(mixed $time)
Return typestringConverts a time in any format to an RSS time. See TimeHelper::toRSS().
SessionHelperclass SessionHelper(View $view, array $settings = array())
As a natural counterpart to the Session Component, the Session Helper replicates most of the component’s functionality
and makes it available in your view.
The major difference between the Session Helper and the Session Component is that the helper does not have the ability
to write to the session.
As with the Session Component, data is read by using dot notation array structures:
array('User' => array('username' => 'super@example.com'));
Given the previous array structure, the node would be accessed by User.username, with the dot indicating the nested
array. This notation is used for all Session helper methods wherever a $key is used.
SessionHelper::read(string $key)
Return type
mixed
Read from the Session. Returns a string or array depending on the contents of the session.
SessionHelper::consume($name)Return type
mixed
Read and delete a value from the Session. This is useful when you want to combine reading and deleting values
in a single operation.
SessionHelper::check(string $key)
Return typebooleanCheck to see whether a key is in the Session. Returns a boolean representing the key’s existence.

More about Views

177CakePHP Cookbook Documentation, Release 2.x

SessionHelper::error()
Return typestringReturns last error encountered in a session.
SessionHelper::valid()
Return typebooleanUsed to check whether a session is valid in a view.
Displaying notifications or flash messages
SessionHelper::flash(string $key = 'flash', array $params = array())
Deprecated since version 2.7.0: You should use FlashHelper to render flash messages.
As explained in Creating notification messages, you can create one-time notifications for feedback. After creating
messages with SessionComponent::setFlash(), you will want to display them. Once a message is displayed,
it will be removed and not displayed again:echo $this->Session->flash();The above will output a simple message with the following HTML:<div id="flashMessage" class="message">
Your stuff has been saved.
</div>
As with the component method, you can set additional properties and customize which element is used. In thecontroller, you might have code like:// in a controller$this->Session->setFlash('The user could not be deleted.');When outputting this message, you can choose the element used to display the message:// in a layout.
echo $this->Session->flash('flash', array('element' => 'failure'));This would use View/Elements/failure.ctp to render the message. The message text would be available as$message in the element.
The failure element would contain something like this:<div class="flash flash-failure"><?php echo h($message); ?></div>
You can also pass additional parameters into the flash() method, which allows you to generate customized
messages:

178

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x// In the controller$this->Session->setFlash('Thanks for your payment.');// In the layout.
echo $this->Session->flash('flash', array('params' => array('name' => $user['User']['name'])'element' => 'payment'));// View/Elements/payment.ctp<div class="flash payment"><?php printf($message, h($name)); ?></div>

Note: By default, CakePHP does not escape the HTML in flash messages. If you are using any request or user
data in your flash messages, you should escape it with h when formatting your messages.

TextHelperclass TextHelper(View $view, array $settings = array())
The TextHelper contains methods to make text more usable and friendly in your views. It aids in enabling links,
formatting URLs, creating excerpts of text around chosen words or phrases, highlighting key words in blocks of text,
and gracefully truncating long stretches of text.
Changed in version 2.1: Several TextHelper methods have been moved into the String class to allow easier use
outside of the View layer. Within a view, these methods are accessible via the TextHelper class. You can call one as
you would call a normal helper method: $this->Text->method($args);.
TextHelper::autoLinkEmails(string $text, array $options=array())
Parameters• $text (string) – The text to convert.
• $options (array) – An array of html attributes for the generated links.
Adds links to the well-formed email addresses in $text, according to any options defined in $options (seeHtmlHelper::link()).
$myText = 'For more information regarding our world-famous ' .
'pastries and desserts, contact info@example.com';$linkedText = $this->Text->autoLinkEmails($myText);
Output:For more information regarding our world-famous pastries and desserts,
contact <a href="mailto:info@example.com">info@example.com</a>Changed in version 2.1: In 2.1 this method automatically escapes its input. Use the escape option to disable
this if necessary.
TextHelper::autoLinkUrls(string $text, array $options=array())
Parameters
More about Views

179

CakePHP Cookbook Documentation, Release 2.x• $text (string) – The text to convert.
• $options (array) – An array html attributes for the generated links
Same as autoLinkEmails(), only this method searches for strings that start with https, http, ftp, or nntp andlinks them appropriately.
Changed in version 2.1: In 2.1 this method automatically escapes its input. Use the escape option to disable
this if necessary.
TextHelper::autoLink(string $text, array $options=array())
Parameters• $text (string) – The text to autolink.
• $options (array) – An array html attributes for the generated links
Performs the functionality in both autoLinkUrls() and autoLinkEmails() on the supplied $text. All URLs
and emails are linked appropriately given the supplied $options.
Changed in version 2.1: As of 2.1, this method automatically escapes its input. Use the escape option to disable
this if necessary.
TextHelper::autoParagraph(string $text)
Parameters• $text (string) – The text to convert.
Adds proper <p> around text where double-line returns are found, and <br> where single-line returns are found.
$myText = 'For more information
regarding our world-famous pastries and desserts.
contact info@example.com';$formattedText = $this->Text->autoParagraph($myText);
Output:<p>For more information<br />regarding our world-famous pastries and desserts.</p><p>contact info@example.com</p>
New in version 2.4.
TextHelper::highlight(string $haystack, string $needle, array $options = array())
Parameters• $haystack (string) – The string to search.
• $needle (string) – The string to find.
• $options (array) – An array of options, see below.
Highlights $needle in $haystack using the $options['format'] string specified or a default string.
Options:• ‘format’ - string The piece of HTML with that the phrase will be highlighted• ‘html’ - bool If true, will ignore any HTML tags, ensuring that only the correct text is highlighted
Example:180

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x// called as TextHelper
echo $this->Text->highlight($lastSentence,'using',array('format' => '<span class="highlight">\1</span>'));// called as CakeText
App::uses('CakeText', 'Utility');echo CakeText::highlight($lastSentence,'using',array('format' => '<span class="highlight">\1</span>'));Output:Highlights $needle in $haystack <span class="highlight">using</span>
the $options['format'] string specified or a default string.
TextHelper::stripLinks($text)
Strips the supplied $text of any HTML links.
TextHelper::truncate(string $text, int $length=100, array $options)Parameters• $text (string) – The text to truncate.
• $length (int) – The length, in characters, beyond which the text should be truncated.
• $options (array) – An array of options to use.
If $text is longer than $length characters, this method truncates it at $length and adds a suffix consisting of'ellipsis', if defined. If 'exact' is passed as false, the truncation will occur at the first whitespace after
the point at which $length is exceeded. If 'html' is passed as true, HTML tags will be respected and will
not be cut off.
$options is used to pass all extra parameters, and has the following possible keys by default, all of which are
optional:
array('ellipsis' => '...','exact' => true,'html' => false)
Example:// called as TextHelper
echo $this->Text->truncate('The killer crept forward and tripped on the rug.',22,
array('ellipsis' => '...','exact' => false(continues on next page)

More about Views

181

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)));// called as CakeText
App::uses('CakeText', 'Utility');echo CakeText::truncate('The killer crept forward and tripped on the rug.',22,
array('ellipsis' => '...','exact' => false));
Output:The killer crept...
Changed in version 2.3: ending has been replaced by ellipsis. ending is still used in 2.2.1
TextHelper::tail(string $text, int $length=100, array $options)Parameters• $text (string) – The text to truncate.
• $length (int) – The length, in characters, beyond which the text should be truncated.
• $options (array) – An array of options to use.
If $text is longer than $length characters, this method removes an initial substring with length consisting of
the difference and prepends a prefix consisting of 'ellipsis', if defined. If 'exact' is passed as false, thetruncation will occur at the first whitespace prior to the point at which truncation would otherwise take place.
$options is used to pass all extra parameters, and has the following possible keys by default, all of which are
optional:
array('ellipsis' => '...','exact' => true)
New in version 2.3.
Example:$sampleText = 'I packed my bag and in it I put a PSP, a PS3, a TV, ' .
'a C# program that can divide by zero, death metal t-shirts'// called as TextHelper
echo $this->Text->tail($sampleText,70,
array('ellipsis' => '...','exact' => false(continues on next page)

182

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)));// called as CakeText
App::uses('CakeText', 'Utility');echo CakeText::tail($sampleText,70,
array('ellipsis' => '...','exact' => false));
Output:...a TV, a C# program that can divide by zero, death metal t-shirts
TextHelper::excerpt(string $haystack, string $needle, integer $radius=100, string $ellipsis="...")
Parameters• $haystack (string) – The string to search.
• $needle (string) – The string to excerpt around.
• $radius (int) – The number of characters on either side of $needle you want to include.
• $ellipsis (string) – Text to append/prepend to the beginning or end of the result.
Extracts an excerpt from $haystack surrounding the $needle with a number of characters on each side determined by $radius, and prefix/suffix with $ellipsis. This method is especially handy for search results. The
query string or keywords can be shown within the resulting document.
// called as TextHelper
echo $this->Text->excerpt($lastParagraph, 'method', 50, '...');// called as CakeText
App::uses('CakeText', 'Utility');echo CakeText::excerpt($lastParagraph, 'method', 50, '...');
Output:... by $radius, and prefix/suffix with $ellipsis. This method is
especially handy for search results. The query...
TextHelper::toList(array $list, $and='and')Parameters• $list (array) – Array of elements to combine into a list sentence.
• $and (string) – The word used for the last join.
Creates a comma-separated list where the last two items are joined with ‘and’.

More about Views

183

CakePHP Cookbook Documentation, Release 2.x// called as TextHelper
echo $this->Text->toList($colors);// called as CakeText
App::uses('CakeText', 'Utility');echo CakeText::toList($colors);
Output:red, orange, yellow, green, blue, indigo and violet

TimeHelperclass TimeHelper(View $view, array $settings = array())
The Time Helper does what it says on the tin: saves you time. It allows for the quick processing of time related
information. The Time Helper has two main tasks that it can perform:
1. It can format time strings.
2. It can test time (but cannot bend time, sorry).
Changed in version 2.1: TimeHelper has been refactored into the CakeTime class to allow easier use outside of theView layer. Within a view, these methods are accessible via the TimeHelper class and you can call it as you would calla normal helper method: $this->Time->method($args);.
Using the Helper
A common use of the Time Helper is to offset the date and time to match a user’s time zone. Lets use a forum as an example. Your forum has many users who may post messages at any time from any part of theworld. An easy way to manage the time is to save all dates and times as GMT+0 or UTC. Uncomment the line
date_default_timezone_set('UTC'); in app/Config/core.php to ensure your application’s time zone is set
to GMT+0.
Next add a time zone field to your users table and make the necessary modifications to allow your users to set their timezone. Now that we know the time zone of the logged in user we can correct the date and time on our posts using theTime Helper:
echo $this->Time->format('F jS, Y h:i A',$post['Post']['created'],null,$user['User']['time_zone']);// Will display August 22nd, 2011 11:53 PM for a user in GMT+0// August 22nd, 2011 03:53 PM for a user in GMT-8// and August 23rd, 2011 09:53 AM GMT+10
Most of the Time Helper methods have a $timezone parameter. The $timezone parameter accepts a valid timezone
identifier string or an instance of DateTimeZone class.

184

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Formatting
TimeHelper::convert($serverTime, $timezone = NULL)
Return type
integerConverts given time (in server’s time zone) to user’s local time, given his/her timezone.
// called via TimeHelper
echo $this->Time->convert(time(), 'Asia/Jakarta');// 1321038036// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::convert(time(), new DateTimeZone('Asia/Jakarta'));Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
TimeHelper::convertSpecifiers($format, $time = NULL)
Return typestringConverts a string representing the format for the function strftime and returns a Windows safe and i18n aware
format.
TimeHelper::dayAsSql($dateString, $field_name, $timezone = NULL)
Return typestringCreates a string in the same format as daysAsSql but only needs a single date object:// called via TimeHelper
echo $this->Time->dayAsSql('Aug 22, 2011', 'modified');// (modified >= '2011-08-22 00:00:00') AND// (modified <= '2011-08-22 23:59:59')// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::dayAsSql('Aug 22, 2011', 'modified');Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
TimeHelper::daysAsSql($begin, $end, $fieldName, $timezone = NULL)
Return typestringReturns a string in the format “($field_name >= ‘2008-01-21 00:00:00’) AND ($field_name <= ‘2008-01-25
23:59:59’)”. This is handy if you need to search for records between two dates inclusively:// called via TimeHelper
echo $this->Time->daysAsSql('Aug 22, 2011', 'Aug 25, 2011', 'created');// (created >= '2011-08-22 00:00:00') AND(continues on next page)

More about Views

185

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// (created <= '2011-08-25 23:59:59')// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::daysAsSql('Aug 22, 2011', 'Aug 25, 2011', 'created');Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
TimeHelper::format($date, $format = NULL, $default = false, $timezone = NULL)
Return typestringWill return a string formatted to the given format using the PHP strftime() formatting options59 :// called via TimeHelper
echo $this->Time->format('2011-08-22 11:53:00', '%B %e, %Y %H:%M %p');// August 22, 2011 11:53 AM
echo $this->Time->format('+2 days', '%c');// 2 days from now formatted as Sun, 13 Nov 2011 03:36:10 AM EET// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::format('2011-08-22 11:53:00', '%B %e, %Y %H:%M %p');
echo CakeTime::format('+2 days', '%c');
You can also provide the date/time as the first argument. When doing this you should use strftime compatible
formatting. This call signature allows you to leverage locale aware date formatting which is not possible using
date() compatible formatting:// called via TimeHelper
echo $this->Time->format('2012-01-13', '%d-%m-%Y', 'invalid');// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::format('2011-08-22', '%d-%m-%Y');
Changed in version 2.2: $format and $date parameters are in opposite order as used in 2.1 and below.
$timezone parameter replaces $userOffset parameter used in 2.1 and below. $default parameter replaces$invalid parameter used in 2.1 and below.
New in version 2.2: $date parameter now also accepts a DateTime object.
TimeHelper::fromString($dateString, $timezone = NULL)
Return typestringTakes a string and uses strtotime60 to convert it into a date integer:
59 https://www.php.net/manual/en/function.strftime.php

186

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x// called via TimeHelper
echo $this->Time->fromString('Aug 22, 2011');// 1313971200
echo $this->Time->fromString('+1 days');// 1321074066 (+1 day from current date)// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::fromString('Aug 22, 2011');echo CakeTime::fromString('+1 days');
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
TimeHelper::gmt($dateString = NULL)
Return type
integerWill return the date as an integer set to Greenwich Mean Time (GMT).
// called via TimeHelper
echo $this->Time->gmt('Aug 22, 2011');// 1313971200// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::gmt('Aug 22, 2011');
TimeHelper::i18nFormat($date, $format = NULL, $invalid = false, $timezone = NULL)
Return typestringReturns a formatted date string, given either a UNIX timestamp or a valid strtotime() date string. It take in accountthe default date format for the current language if a LC_TIME file is used. For more info about LC_TIME file
check here.
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
TimeHelper::nice($dateString = NULL, $timezone = NULL, $format = null)
Return typestringTakes a date string and outputs it in the format “Tue, Jan 1st 2008, 19:25” or as per optional $format parampassed:// called via TimeHelper
echo $this->Time->nice('2011-08-22 11:53:00');// Mon, Aug 22nd 2011, 11:53// called as CakeTime(continues on next page)60 https://us.php.net/manual/en/function.date.php

More about Views

187

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

App::uses('CakeTime', 'Utility');echo CakeTime::nice('2011-08-22 11:53:00');
TimeHelper::niceShort($dateString = NULL, $timezone = NULL)
Return typestringTakes a date string and outputs it in the format “Jan 1st 2008, 19:25”. If the date object is today, the format willbe “Today, 19:25”. If the date object is yesterday, the format will be “Yesterday, 19:25”:// called via TimeHelper
echo $this->Time->niceShort('2011-08-22 11:53:00');// Aug 22nd, 11:53// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::niceShort('2011-08-22 11:53:00');
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
TimeHelper::serverOffset()
Return type
integer
Returns server’s offset from GMT in seconds.
TimeHelper::timeAgoInWords($dateString, $options = array())
Return typestringWill take a datetime string (anything that is parsable by PHP’s strtotime() function or MySQL’s datetime format)
and convert it into a friendly word format like, “3 weeks, 3 days ago”:// called via TimeHelper
echo $this->Time->timeAgoInWords('Aug 22, 2011');// on 22/8/11// on August 22nd, 2011
echo $this->Time->timeAgoInWords('Aug 22, 2011',
array('format' => 'F jS, Y'));// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::timeAgoInWords('Aug 22, 2011');echo CakeTime::timeAgoInWords('Aug 22, 2011',
array('format' => 'F jS, Y'));
Use the ‘end’ option to determine the cutoff point to no longer will use words; default ‘+1 month’:
188

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x// called via TimeHelper
echo $this->Time->timeAgoInWords('Aug 22, 2011',
array('format' => 'F jS, Y', 'end' => '+1 year'));// On Nov 10th, 2011 it would display: 2 months, 2 weeks, 6 days ago// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::timeAgoInWords('Aug 22, 2011',
array('format' => 'F jS, Y', 'end' => '+1 year'));
Use the ‘accuracy’ option to determine how precise the output should be. You can use this to limit the output:// If $timestamp is 1 month, 1 week, 5 days and 6 hours ago
echo CakeTime::timeAgoInWords($timestamp, array('accuracy' => array('month' => 'month'),'end' => '1 year'));// Outputs '1 month ago'
Changed in version 2.2: The accuracy option was added.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
TimeHelper::toAtom($dateString, $timezone = NULL)
Return typestringWill return a date string in the Atom format “2008-01-12T00:00:00Z”
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
TimeHelper::toQuarter($dateString, $range = false)
Return type
mixed
Will return 1, 2, 3 or 4 depending on what quarter of the year the date falls in. If range is set to true, a two element
array will be returned with start and end dates in the format “2008-03-31”:// called via TimeHelper
echo $this->Time->toQuarter('Aug 22, 2011');// Would print 3$arr = $this->Time->toQuarter('Aug 22, 2011', true);
/*
Array
(
[0] => 2011-07-01[1] => 2011-09-30)(continues on next page)

More about Views

189

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)*/// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::toQuarter('Aug 22, 2011');$arr = CakeTime::toQuarter('Aug 22, 2011', true);
New in version 2.2: $dateString parameter now also accepts a DateTime object.
New in version 2.4: The new option parameters relativeString (defaults to %s ago) and absoluteString(defaults to on %s) to allow customization of the resulting output string are now available.
TimeHelper::toRSS($dateString, $timezone = NULL)
Return typestringWill return a date string in the RSS format “Sat, 12 Jan 2008 00:00:00 -0500”
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
TimeHelper::toUnix($dateString, $timezone = NULL)
Return type
integer
A wrapper for fromString.
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
TimeHelper::toServer($dateString, $timezone = NULL, $format = 'Y-m-d H:i:s')
Return typemixed
New in version 2.2: Returns a formatted date in server’s timezone.
TimeHelper::timezone($timezone = NULL)
Return type
DateTimeZone
New in version 2.2: Returns a timezone object from a string or the user’s timezone object. If the function iscalled without a parameter it tries to get timezone from ‘Config.timezone’ configuration variable.
TimeHelper::listTimezones($filter = null, $country = null, $options = array())
Return typearray
New in version 2.2: Returns a list of timezone identifiers.
Changed in version 2.8: $options now accepts array with group, abbr, before, and after keys. Specify
abbr => true will append the timezone abbreviation in the <option> text.

190

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Testing Time
TimeHelper::isToday($dateString, $timezone = NULL)
TimeHelper::isThisWeek($dateString, $timezone = NULL)
TimeHelper::isThisMonth($dateString, $timezone = NULL)
TimeHelper::isThisYear($dateString, $timezone = NULL)
TimeHelper::wasYesterday($dateString, $timezone = NULL)
TimeHelper::isTomorrow($dateString, $timezone = NULL)
TimeHelper::isFuture($dateString, $timezone = NULL)
New in version 2.4.
TimeHelper::isPast($dateString, $timezone = NULL)
New in version 2.4.
TimeHelper::wasWithinLast($timeInterval, $dateString, $timezone = NULL)
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
All of the above functions return true or false when passed a date string. wasWithinLast takes an additional$timeInterval option:// called via TimeHelper$this->Time->wasWithinLast($timeInterval, $dateString);// called as CakeTime
App::uses('CakeTime', 'Utility');
CakeTime::wasWithinLast($timeInterval, $dateString);
wasWithinLast takes a time interval which is a string in the format “3 months” and accepts a time interval of
seconds, minutes, hours, days, weeks, months and years (plural and not). If a time interval is not recognized (forexample, if it is mistyped) then it will default to days.
Using and Configuring Helpers
You enable helpers in CakePHP by making a controller aware of them. Each controller has a $helpers property thatlists the helpers to be made available in the view. To enable a helper in your view, add the name of the helper to thecontroller’s $helpers array:class BakeriesController extends AppController {public $helpers = array('Form', 'Html', 'Js', 'Time');}Adding helpers from plugins uses the plugin syntax used elsewhere in CakePHP:
class BakeriesController extends AppController {public $helpers = array('Blog.Comment');}

More about Views

191

CakePHP Cookbook Documentation, Release 2.x

You can also add helpers from within an action, so they will only be available to that action and not to the other actions in
the controller. This saves processing power for the other actions that do not use the helper and helps keep the controller
better organized:
class BakeriesController extends AppController {public function bake() {$this->helpers[] = 'Time';}
public function mix() {// The Time helper is not loaded here and thus not available}}
If you need to enable a helper for all controllers, add the name of the helper to the $helpers array in /app/
Controller/AppController.php (or create it if not present). Remember to include the default Html and Formhelpers:
class AppController extends Controller {public $helpers = array('Form', 'Html', 'Js', 'Time');}
You can pass options to helpers. These options can be used to set attribute values or modify behavior of a helper:class AwesomeHelper extends AppHelper {public function __construct(View $view, $settings = array()) {parent::__construct($view, $settings);
debug($settings);}}
class AwesomeController extends AppController {public $helpers = array('Awesome' => array('option1' => 'value1'));}
As of 2.3, the options are merged with the Helper::$settings property of the helper.
One common setting to use is the className option, which allows you to create aliased helpers in your views. Thisfeature is useful when you want to replace $this->Html or another common Helper reference with a custom implementation:// app/Controller/PostsController.php
class PostsController extends AppController {public $helpers = array('Html' => array('className' => 'MyHtml'));}// app/View/Helper/MyHtmlHelper.php
App::uses('HtmlHelper', 'View/Helper');class MyHtmlHelper extends HtmlHelper {// Add your code to override the core HtmlHelper}

192

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

The above would alias MyHtmlHelper to $this->Html in your views.
Note: Aliasing a helper replaces that instance anywhere that helper is used, including inside other Helpers.
Using helper settings allows you to declaratively configure your helpers and keep configuration logic out of your controller actions. If you have configuration options that cannot be included as part of a class declaration, you can set thosein your controller’s beforeRender callback:class PostsController extends AppController {public function beforeRender() {parent::beforeRender();$this->helpers['CustomStuff'] = $this->_getCustomStuffSettings();}}

Using Helpers
Once you’ve configured which helpers you want to use in your controller, each helper is exposed as a public property
in the view. For example, if you were using the HtmlHelper you would be able to access it by doing the following:echo $this->Html->css('styles');
The above would call the css method on the HtmlHelper.
You can access any loaded helper using$this->{$helperName}. There may come a time where you need to dynamically load a helper from inside a view.
You can use the view’s HelperCollection to do this:$mediaHelper = $this->Helpers->load('Media', $mediaSettings);
The HelperCollection is a collection and supports the collection API used elsewhere in CakePHP.
Callback methods
Helpers feature several callbacks that allow you to augment the view rendering process. See the Helper API and theCollections documentation for more information.
Creating Helpers
If a core helper (or one showcased on GitHub or in the Bakery) doesn’t fit your needs, helpers are easy to create.
Let’s say we wanted to create a helper that could be used to output a specifically crafted CSS-styled link you needed
many different places in your application. In order to fit your logic into CakePHP’s existing helper structure, you’ll
need to create a new class in /app/View/Helper. Let’s call our helper LinkHelper. The actual PHP class file would
look something like this:/* /app/View/Helper/LinkHelper.php */App::uses('AppHelper', 'View/Helper');class LinkHelper extends AppHelper {public function makeEdit($title, $url) {// Logic to create specially formatted link goes here...
(continues on next page)

More about Views

193

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}}

Note: Helpers must extend either AppHelper or Helper or implement all the callbacks in the Helper API.

Including other Helpers
You may wish to use some functionality already existing in another helper. To do so, you can specify helpers you wish
to use with a $helpers array, formatted just as you would in a controller:/* /app/View/Helper/LinkHelper.php (using other helpers) */App::uses('AppHelper', 'View/Helper');class LinkHelper extends AppHelper {public $helpers = array('Html');public function makeEdit($title, $url) {// Use the HTML helper to output// formatted data:$link = $this->Html->link($title, $url, array('class' => 'edit'));return '<div class="editOuter">' . $link . '</div>';}}

Using your Helper
Once you’ve created your helper and placed it in /app/View/Helper/, you’ll be able to include it in your controllers
using the special variable $helpers:class PostsController extends AppController {public $helpers = array('Link');}
Once your controller has been made aware of this new class, you can use it in your views by accessing an object named
after the helper:<!-- make a link using the new helper --><?php echo $this->Link->makeEdit('Change this Recipe', '/recipes/edit/5'); ?>194

Chapter 5. Views

CakePHP Cookbook Documentation, Release 2.x

Creating Functionality for All Helpers
All helpers extend a special class, AppHelper (just like models extend AppModel and controllers extend AppController). To create functionality that would be available to all helpers, create /app/View/Helper/AppHelper.php:
App::uses('Helper', 'View');
class AppHelper extends Helper {public function customMethod() {}}Helper API
class Helper
The base class for Helpers. It provides a number of utility methods and features for loading other helpers.
Helper::webroot($file)
Resolve a file name to the webroot of the application. If a theme is active and the file exists in the current theme’s
webroot, the path to the themed file will be returned.
Helper::url($url, $full = false)
Generates an HTML escaped URL, delegates to Router::url().
Helper::value($options = array(), $field = null, $key = 'value')Get the value for a given input name.
Helper::domId($options = null, $id = 'id')Generate a CamelCased id value for the currently selected field. Overriding this method in your AppHelper willallow you to change how CakePHP generates ID attributes.
Callbacks
Helper::beforeRenderFile($viewFile)Is called before each view file is rendered. This includes elements, views, parent views and layouts.
Helper::afterRenderFile($viewFile, $content)
Is called after each view file is rendered. This includes elements, views, parent views and layouts. A callback
can modify and return $content to change how the rendered content will be displayed in the browser.
Helper::beforeRender($viewFile)
The beforeRender method is called after the controller’s beforeRender method but before the controller renders
view and layout. Receives the file being rendered as an argument.
Helper::afterRender($viewFile)Is called after the view has been rendered but before layout rendering has started.
Helper::beforeLayout($layoutFile)Is called before layout rendering starts. Receives the layout filename as an argument.
Helper::afterLayout($layoutFile)Is called after layout rendering is complete. Receives the layout filename as an argument.

More about Views

195

CakePHP Cookbook Documentation, Release 2.x

196

Chapter 5. Views

CHAPTER 6

ModelsModels are the classes that form the business layer in your application. They should be responsible for managing almost
everything regarding your data, its validity, and its interactions, as well as the evolution of the information workflow inyour domain.
Usually, model classes represent data and are used in CakePHP applications for data access. They generally represent
a database table but can be used to access anything that manipulates data such as files, external web services, or iCal
events.
A model can be associated with other models. For example, a Recipe may be associated with an Author as well as an
Ingredient.
This section will explain what features of the model can be automated, how to override those features, and what methods
and properties a model can have. It will explain the different ways to build associations for your data. It will describe
how to find, save, and delete data. Finally, it will look at Datasources.

Understanding Models
A Model represents your data model. In object-oriented programming, a data model is an object that represents a thingsuch as a car, a person, or a house. A blog, for example, may have many blog posts and each blog post may have many
comments. The Blog, Post, and Comment are all examples of models, each associated with another.
Here is a simple example of a model definition in CakePHP:
App::uses('AppModel', 'Model');class Ingredient extends AppModel {public $name = 'Ingredient';}
With just this simple declaration, the Ingredient model is endowed with all the functionality you need to create queries
and to save and delete data. These methods come from CakePHP’s Model class by the magic of inheritance. The
Ingredient model extends the application model, AppModel, which in turn extends CakePHP’s internal Model class.
197

CakePHP Cookbook Documentation, Release 2.x

It is this core Model class that bestows the functionality onto your Ingredient model. App::uses('AppModel','Model') ensures that the model is loaded when it is needed.
The intermediate class, AppModel, is empty. If you haven’t created your own, it is taken from the CakePHP core folder.
Overriding the AppModel allows you to define functionality that should be made available to all models within yourapplication. To do so, you need to create your own AppModel.php file that resides in the Model folder, as do all othermodels in your application. Creating a project using Bake will automatically generate this file for you.
See also Behaviors for more information on how to apply similar logic to multiple models.
Back to our Ingredient model. In order to work on it, create the PHP file in the /app/Model/ directory. By convention,
it should have the same name as the class, which for this example will be Ingredient.php.
Note: CakePHP will dynamically create a model object for you if it cannot find a corresponding file in /app/Model.
This also means that if your model file isn’t named correctly (for instance, if it is named ingredient.php or Ingredients.php rather than Ingredient.php), CakePHP will use an instance of AppModel rather than your model file (which
CakePHP assumes is missing). If you’re trying to use a method you’ve defined in your model, or a behavior attached toyour model, and you’re getting SQL errors that are the name of the method you’re calling, it’s a sure sign that CakePHP
can’t find your model and you need to check the file names, your application cache, or both.

Note: Some class names are not usable for model names. For instance, “File” cannot be used, since “File” is a classthat already exists in the CakePHP core.
When your model is defined, it can be accessed from within your Controller. CakePHP will automatically makethe model available for access when its name matches that of the controller. For example, a controller named IngredientsController will automatically initialize the Ingredient model and attach it to the controller at $this->Ingredient:class IngredientsController extends AppController {public function index() {//grab all ingredients and pass it to the view:$ingredients = $this->Ingredient->find('all');$this->set('ingredients', $ingredients);}}
Associated models are available through the main model. In the following example, Recipe has an association with the
Ingredient model:
class Recipe extends AppModel {public function steakRecipes() {$ingredient = $this->Ingredient->findByName('Steak');
return $this->findAllByMainIngredient($ingredient['Ingredient']['id']);}}This shows how to use models that are already linked. To understand how associations are defined, take a look at the
Associations section

198

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

More on models
Associations: Linking Models Together
One of the most powerful features of CakePHP is the ability to link relational mapping provided by the model. In
CakePHP, the links between models are handled through associations.
Defining relations between different objects in your application should be a natural process. For example: in a recipe
database, a recipe may have many reviews, reviews have a single author, and authors may have many recipes. Defining
the way these relations work allows you to access your data in an intuitive and powerful way.
The purpose of this section is to show you how to plan for, define, and utilize associations between models in CakePHP.
While data can come from a variety of sources, the most common form of storage in web applications is a relational
database. Most of what this section covers will be in that context.
For information on associations with Plugin models, see Plugin Models.
Relationship Types
The four association types in CakePHP are: hasOne, hasMany, belongsTo, and hasAndBelongsToMany (HABTM).
Relationship

Association Type

Exampleone to one
one to many
many to one
many to many

hasOnehasMany
belongsTo
hasAndBelongsToMany

A user has one profile.
A user can have multiple recipes.
Many recipes belong to a user.
Recipes have, and belong to, many ingredients.

To further clarify which way around the associations are defined in the models: If the table of the model contains theforeign key (other_model_id), the relation type in this model is always a Model belongsTo OtherModel relation!
Associations are defined by creating a class variable named after the association you are defining. The class variable
can sometimes be as simple as a string, but can be as complex as a multidimensional array used to define association
specifics.
class User extends AppModel {public $hasOne = 'Profile';
public $hasMany = array('Recipe' => array('className' => 'Recipe','conditions' => array('Recipe.approved' => '1'),'order' => 'Recipe.created DESC'));}
In the above example, the first instance of the word ‘Recipe’ is what is termed an ‘Alias’. This is an identifier for the
relationship, and can be anything you choose. Usually, you will choose the same name as the class that it references.
However, aliases for each model must be unique across the app. For example, it is appropriate to have:class User extends AppModel {public $hasMany = array('MyRecipe' => array((continues on next page)

More on models

199

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'className' => 'Recipe',));
public $hasAndBelongsToMany = array('MemberOf' => array('className' => 'Group',));}class Group extends AppModel {public $hasMany = array('MyRecipe' => array('className' => 'Recipe',));
public $hasAndBelongsToMany = array('Member' => array('className' => 'User',));}but the following will not work well in all circumstances:class User extends AppModel {public $hasMany = array('MyRecipe' => array('className' => 'Recipe',));
public $hasAndBelongsToMany = array('Member' => array('className' => 'Group',));}class Group extends AppModel {public $hasMany = array('MyRecipe' => array('className' => 'Recipe',));
public $hasAndBelongsToMany = array('Member' => array('className' => 'User',));}because here we have the alias ‘Member’ referring to both the User (in Group) and the Group (in User) model in the
HABTM associations. Choosing non-unique names for model aliases across models can cause unexpected behavior.
200

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

CakePHP will automatically create links between associated model objects. So for example in your User model you
can access the Recipe model as:$this->Recipe->someFunction();Similarly in your controller you can access an associated model simply by following your model associations:$this->User->Recipe->someFunction();Note: Remember that associations are defined ‘one way’. If you define User hasMany Recipe, that has no effect on
the Recipe Model. You need to define Recipe belongsTo User to be able to access the User model from your Recipemodel.

hasOneLet’s set up a User model with a hasOne relationship to a Profile model.
First, your database tables need to be keyed correctly. For a hasOne relationship to work, one table has to contain aforeign key that points to a record in the other. In this case, the profiles table will contain a field called user_id. The
basic pattern is:
hasOne: the other model contains the foreign key.
Relation

SchemaApple hasOne Banana
User hasOne Profile
Doctor hasOne Mentor

bananas.apple_id
profiles.user_id
mentors.doctor_id

Note: It is not mandatory to follow CakePHP conventions. You can easily override the use of any foreignKey in yourassociations definitions. Nevertheless, sticking to conventions will make your code less repetitive and easier to readand maintain.
The User model file will be saved in /app/Model/User.php. To define the ‘User hasOne Profile’ association, add the$hasOne property to the model class. Remember to have a Profile model in /app/Model/Profile.php, or the association
won’t work:class User extends AppModel {public $hasOne = 'Profile';}
There are two ways to describe this relationship in your model files. The simplest method is to set the $hasOne attributeto a string containing the class name of the associated model, as we’ve done above.
If you need more control, you can define your associations using array syntax. For example, you might want to limit
the association to include only certain records.
class User extends AppModel {public $hasOne = array('Profile' => array('className' => 'Profile',(continues on next page)

More on models

201

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'conditions' => array('Profile.published' => '1'),'dependent' => true));}
Possible keys for hasOne association arrays include:• className: the class name of the model being associated to the current model. If you’re defining a ‘User hasOne
Profile’ relationship, the className key should equal ‘Profile’.
• foreignKey: the name of the foreign key found in the other model. This is especially handy if you need to define
multiple hasOne relationships. The default value for this key is the underscored, singular name of the current
model, suffixed with ‘_id’. In the example above, it would default to ‘user_id’. If the other model uses anothername than ‘id’ for the primary key, then make sure to specify it using the $primaryKey property of your Modelclass (in the example above, in the ‘Profile’ Model class). Otherwise cascade deletes will not work.
• conditions: an array of find()-compatible conditions or SQL strings such as array(‘Profile.approved’ => true)• fields: A list of fields to be retrieved when the associated model data is fetched. Returns all fields by default.
• order: an array of find()-compatible order clauses or SQL strings such as array(‘Profile.last_name’ => ‘ASC’)• dependent: When the dependent key is set to true, and the model’s delete() method is called with the cascadeparameter set to true, associated model records are also deleted. In this case, we set it true so that deleting a User
will also delete her associated Profile.
Once this association has been defined, find operations on the User model will also fetch a related Profile record if itexists://Sample results from a $this->User->find() call.
Array([User] => Array([id] => 121[name] => Gwoo the Kungwoo[created] => 2007-05-01 10:31:01)[Profile] => Array([id] => 12[user_id] => 121[skill] => Baking Cakes[created] => 2007-05-01 10:31:01
)
)

202

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

belongsTo
Now that we have Profile data access from the User model, let’s define a belongsTo association in the Profile model inorder to get access to related User data. The belongsTo association is a natural complement to the hasOne and hasMany
associations: it allows us to see the data from the other direction.
When keying your database tables for a belongsTo relationship, follow this convention:
belongsTo: the current model contains the foreign key.
Relation

SchemaBanana belongsTo Apple
Profile belongsTo User
Mentor belongsTo Doctor

bananas.apple_id
profiles.user_id
mentors.doctor_id

Tip: If a model(table) contains a foreign key, it belongsTo the other model(table).
We can define the belongsTo association in our Profile model at /app/Model/Profile.php using the string syntax asfollows:class Profile extends AppModel {public $belongsTo = 'User';}
We can also define a more specific relationship using array syntax:
class Profile extends AppModel {public $belongsTo = array('User' => array('className' => 'User','foreignKey' => 'user_id'));}
Possible keys for belongsTo association arrays include:• className: the class name of the model being associated to the current model. If you’re defining a ‘Profile
belongsTo User’ relationship, the className key should equal ‘User’.
• foreignKey: the name of the foreign key found in the current model. This is especially handy if you need todefine multiple belongsTo relationships. The default value for this key is the underscored, singular name of theother model, suffixed with _id.
• conditions: an array of find() compatible conditions or SQL strings such as array('User.active' => true)• type: the type of the join to use in the SQL query. The default is ‘LEFT’, which may not fit your needs in all
situations. The value ‘INNER’ may be helpful (when used with some conditions) when you want everythingfrom your main and associated models or nothing at all.
• fields: A list of fields to be retrieved when the associated model data is fetched. Returns all fields by default.
• order: an array of find() compatible order clauses or SQL strings such as array('User.username' =>'ASC')

More on models

203

CakePHP Cookbook Documentation, Release 2.x• counterCache: If set to true, the associated Model will automatically increase or decrease the “[singular_model_name]_count” field in the foreign table whenever you do a save() or delete(). If it’s a string,then it’s the field name to use. The value in the counter field represents the number of related rows. You can also
specify multiple counter caches by defining an array. See Multiple counterCache.
• counterScope: Optional conditions array to use for updating counter cache field.
Once this association has been defined, find operations on the Profile model will also fetch a related User record if itexists://Sample results from a $this->Profile->find() call.
Array([Profile] => Array([id] => 12[user_id] => 121[skill] => Baking Cakes[created] => 2007-05-01 10:31:01)[User] => Array([id] => 121[name] => Gwoo the Kungwoo[created] => 2007-05-01 10:31:01))

counterCache - Cache your count()
This feature helps you cache the count of related data. Instead of counting the records manually via find('count'),
the model itself tracks any addition/deletion towards the associated $hasMany model and increases/decreases a dedicated integer field within the parent model table.
The name of the field consists of the singular model name followed by a underscore and the word “count”:
my_model_countLet’s say you have a model called ImageComment and a model called Image. You would add a new INT-field to theimages table and name it image_comment_count.
Here are some more examples:
Model

Associated Model

ExampleUser
Image
BlogEntry

Image
ImageComment
BlogEntryComment

users.image_countimages.image_comment_count
blog_entries.blog_entry_comment_countOnce you have added the counter field, you are good to go. Activate counter-cache in your association by adding acounterCache key and set the value to true:

204

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

class ImageComment extends AppModel {public $belongsTo = array('Image' => array('counterCache' => true,));}
From now on, every time you add or remove a ImageComment associated to Image, the number within
image_comment_count is adjusted automatically.
counterScope
You can also specify counterScope. It allows you to specify a simple condition which tells the model when to update(or when not to, depending on how you look at it) the counter value.
Using our Image model example, we can specify it like so:class ImageComment extends AppModel {public $belongsTo = array('Image' => array('counterCache' => 'active_comment_count', //custom field name// only count if "ImageComment" is active = 1'counterScope' => array('ImageComment.active' => 1
)
)
);
}

Multiple counterCache
Since 2.0, CakePHP has supported having multiple counterCache in a single model relation. It is also possible todefine a counterScope for each counterCache. Assuming you have a User model and a Message model, and you
want to be able to count the amount of read and unread messages for each user.
Model

Field

Description

User
User
Messageusers.messages_read
users.messages_unread
messages.is_read

Count read Message
Count unread Message
Determines if a Message is read or not.

With this setup, your belongsTo would look like this:class Message extends AppModel {public $belongsTo = array('User' => array('counterCache' => array('messages_read' => array('Message.is_read' => 1),'messages_unread' => array('Message.is_read' => 0))(continues on next page)

More on models

205CakePHP Cookbook Documentation, Release 2.x(continued from previous page)));}

hasMany
Next step: defining a “User hasMany Comment” association. A hasMany association will allow us to fetch a user’scomments when we fetch a User record.
When keying your database tables for a hasMany relationship, follow this convention:
hasMany: the other model contains the foreign key.
Relation

SchemaUser hasMany Comment
Cake hasMany Virtue
Product hasMany Option

Comment.user_id
Virtue.cake_id
Option.product_id

We can define the hasMany association in our User model at /app/Model/User.php using the string syntax as follows:class User extends AppModel {public $hasMany = 'Comment';}
We can also define a more specific relationship using array syntax:
class User extends AppModel {public $hasMany = array('Comment' => array('className' => 'Comment','foreignKey' => 'user_id','conditions' => array('Comment.status' => '1'),'order' => 'Comment.created DESC','limit' => '5',
'dependent' => true));}
Possible keys for hasMany association arrays include:• className: the class name of the model being associated to the current model. If you’re defining a ‘User
hasMany Comment’ relationship, the className key should equal ‘Comment.’• foreignKey: the name of the foreign key found in the other model. This is especially handy if you need to define
multiple hasMany relationships. The default value for this key is the underscored, singular name of the actualmodel, suffixed with ‘_id’.
• conditions: an array of find() compatible conditions or SQL strings such as array(‘Comment.visible’ => true)• order: an array of find() compatible order clauses or SQL strings such as array(‘Profile.last_name’ => ‘ASC’)• limit: The maximum number of associated rows you want returned.

206

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x• offset: The number of associated rows to skip over (given the current conditions and order) before fetching andassociating.
• dependent: When dependent is set to true, recursive model deletion is possible. In this example, Comment
records will be deleted when their associated User record has been deleted.
• exclusive: When exclusive is set to true, recursive model deletion does the delete with a deleteAll() call, insteadof deleting each entity separately. This greatly improves performance, but may not be ideal for all circumstances.
• finderQuery: A complete SQL query CakePHP can use to fetch associated model records. This should be
used in situations that require highly customized results. If a query you’re building requires a reference to theassociated model ID, use the special {$__cakeID__$} marker in the query. For example, if your Apple model
hasMany Orange, the query should look something like this: SELECT Orange.* from oranges as Orange
WHERE Orange.apple_id = {$__cakeID__$};
Once this association has been defined, find operations on the User model will also fetch related Comment records ifthey exist://Sample results from a $this->User->find() call.
Array([User] => Array([id] => 121[name] => Gwoo the Kungwoo[created] => 2007-05-01 10:31:01)[Comment] => Array([0] => Array([id] => 123[user_id] => 121[title] => On Gwoo the Kungwoo[body] => The Kungwooness is not so Gwooish[created] => 2006-05-01 10:31:01)[1] => Array([id] => 124[user_id] => 121[title] => More on Gwoo
[body] => But what of the 'Nut?
[created] => 2006-05-01 10:41:01)))
One thing to remember is that you’ll need a complementary Comment belongsTo User association in order to get the datafrom both directions. What we’ve outlined in this section empowers you to get Comment data from the User. Adding
the Comment belongsTo User association in the Comment model enables you to get User data from the Comment
model, completing the connection and allowing the flow of information from either model’s perspective.

More on models

207

CakePHP Cookbook Documentation, Release 2.x

hasAndBelongsToMany (HABTM)
All right. At this point, you can already call yourself a CakePHP model associations professional. You’re already wellversed in the three associations that take up the bulk of object relations.
Let’s tackle the final relationship type: hasAndBelongsToMany, or HABTM. This association is used when you have
two models that need to be joined up, repeatedly, many times, in many different ways.
The main difference between hasMany and HABTM is that a link between models in HABTM is not exclusive. For
example, we’re about to join up our Recipe model with an Ingredient model using HABTM. Using tomatoes as an
Ingredient for my grandma’s spaghetti recipe doesn’t “use up” the ingredient. I can also use it for a salad Recipe.
Links between hasMany associated objects are exclusive. If my User hasMany Comments, a comment is only linked
to a specific user. It’s not up for grabs.
Moving on. We’ll need to set up an extra table in the database to handle HABTM associations. This new join table’s
name needs to include the names of both models involved, in alphabetical order, and separated with an underscore ( _ ).
The contents of the table should be two fields that are foreign keys (which should be integers) pointing to the primarykeys of the involved models. To avoid any issues, don’t define a combined primary key for these two fields. If yourapplication requires a unique index, you can define one. If you plan to add any extra information to this table, or use a‘with’ model, you should add an additional primary key field (by convention ‘id’).
HABTM requires a separate join table that includes both model names.
Relationship

HABTM Table Fields

Recipe HABTM IngredientCake HABTM Fan
Foo HABTM Bar

ingredients_recipes.id,
ingredients_recipes.ingredient_id,
ents_recipes.recipe_id
cakes_fans.id, cakes_fans.cake_id, cakes_fans.fan_id
bars_foos.id, bars_foos.foo_id, bars_foos.bar_id

ingredi-Note: Table names are in alphabetical order by convention. It is possible to define a custom table name in association
definition.
Make sure primary keys in tables cakes and recipes have “id” fields as assumed by convention. If they’re different thanassumed, they must be changed in model’s primaryKey.
Once this new table has been created, we can define the HABTM association in the model files. We’re going to skip
straight to the array syntax this time:class Recipe extends AppModel {public $hasAndBelongsToMany = array('Ingredient' =>array('className' => 'Ingredient','joinTable' => 'ingredients_recipes','foreignKey' => 'recipe_id','associationForeignKey' => 'ingredient_id','unique' => true,'conditions' => '','fields' => '','order' => '','limit' => '','offset' => '',(continues on next page)

208

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'finderQuery' => '','with' => ''));}Possible keys for HABTM association arrays include:• className: the class name of the model being associated to the current model. If you’re defining a ‘Recipe
HABTM Ingredient’ relationship, the className key should equal ‘Ingredient’.
• joinTable: The name of the join table used in this association (if the current table doesn’t adhere to the naming
convention for HABTM join tables).
• with: Defines the name of the model for the join table. By default CakePHP will auto-create a model for you.
Using the example above it would be called IngredientsRecipe. By using this key you can override this default
name. The join table model can be used just like any “regular” model to access the join table directly. By creating
a model class with such name and filename, you can add any custom behavior to the join table searches, such asadding more information/columns to it.
• foreignKey: the name of the foreign key found in the current model. This is especially handy if you need todefine multiple HABTM relationships. The default value for this key is the underscored, singular name of thecurrent model, suffixed with ‘_id’.
• associationForeignKey: the name of the foreign key found in the other model. This is especially handy if you
need to define multiple HABTM relationships. The default value for this key is the underscored, singular nameof the other model, suffixed with ‘_id’.
• unique: boolean or string keepExisting.
– If true (default value) CakePHP will first delete existing relationship records in the foreign keys table
before inserting new ones. Existing associations need to be passed again when updating.
– When false, CakePHP will insert the specified new relationship records and leave any existing relationship records in place, possibly resulting in duplicate relationship records.
– When set to keepExisting, the behavior is similar to true, but with an additional check so that if anyof the records to be added are duplicates of an existing relationship record, the existing relationshiprecord is not deleted, and the duplicate is ignored. This can be useful if, for example, the join table has
additional data in it that needs to be retained.
• conditions: an array of find()-compatible conditions or SQL string. If you have conditions on an associatedtable, you should use a ‘with’ model, and define the necessary belongsTo associations on it.
• fields: A list of fields to be retrieved when the associated model data is fetched. Returns all fields by default.
• order: an array of find()-compatible order clauses or SQL strings• limit: The maximum number of associated rows you want returned.
• offset: The number of associated rows to skip over (given the current conditions and order) before fetching andassociating.
• finderQuery: A complete SQL query CakePHP can use to fetch associated model records. This should be usedin situations that require highly customized results.
Once this association has been defined, find operations on the Recipe model will also fetch related Ingredient records
if they exist:

More on models

209

CakePHP Cookbook Documentation, Release 2.x// Sample results from a $this->Recipe->find() call.
Array([Recipe] => Array([id] => 2745
[name] => Chocolate Frosted Sugar Bombs[created] => 2007-05-01 10:31:01[user_id] => 2346)
[Ingredient] => Array([0] => Array([id] => 123[name] => Chocolate)[1] => Array([id] => 124[name] => Sugar)[2] => Array([id] => 125[name] => Bombs)))
Remember to define a HABTM association in the Ingredient model if you’d like to fetch Recipe data when using the
Ingredient model.
Note: HABTM data is treated like a complete set. Each time a new data association is added, the complete set of
associated rows in the database is dropped and created again so you will always need to pass the whole data set forsaving. For an alternative to using HABTM, see hasMany through (The Join Model).

Tip: For more information on saving HABTM objects, see Saving Related Model Data (HABTM)

210

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

hasMany through (The Join Model)
It is sometimes desirable to store additional data with a many-to-many association. Consider the following
Student hasAndBelongsToMany Course
Course hasAndBelongsToMany Student
In other words, a Student can take many Courses and a Course can be taken by many Students. This is a simple
many-to-many association demanding a table such as this:id | student_id | course_id
Now what if we want to store the number of days that were attended by the student on the course and their final grade?
The table we’d want would be:
id | student_id | course_id | days_attended | grade
The trouble is, hasAndBelongsToMany will not support this type of scenario because when hasAndBelongsToMany
associations are saved, the association is deleted first. You would lose the extra data in the columns as it is not replacedin the new insert.
Changed in version 2.1: You can set the unique setting to keepExisting to circumvent losing extra data during thesave operation. See unique key in HABTM association arrays.
The way to implement our requirement is to use a join model, otherwise known as a hasMany through association.
That is, the association is a model itself. So, we can create a new model CourseMembership. Take a look at thefollowing models.
// Student.php
class Student extends AppModel {public $hasMany = array('CourseMembership');}// Course.php
class Course extends AppModel {public $hasMany = array('CourseMembership');}// CourseMembership.php
class CourseMembership extends AppModel {public $belongsTo = array('Student', 'Course');}
The CourseMembership join model uniquely identifies a given Student’s participation on a Course in addition to extra
meta-information.
Join models are pretty useful things to be able to use, and CakePHP makes it easy to do so with its built-in hasMany
and belongsTo associations and saveAll feature.

More on models

211CakePHP Cookbook Documentation, Release 2.x

Creating and Destroying Associations on the Fly
Sometimes it becomes necessary to create and destroy model associations on the fly. This may be for any number ofreasons:• You want to reduce the amount of associated data fetched, but all your associations are on the first level ofrecursion.
• You want to change the way an association is defined in order to sort or filter associated data.
This association creation and destruction is done using the CakePHP model bindModel() and unbindModel() methods.
(There is also a very helpful behavior called “Containable”. Please refer to the manual section about Built-in behaviors
for more information.) Let’s set up a few models so we can see how bindModel() and unbindModel() work. We’ll startwith two models:class Leader extends AppModel {public $hasMany = array('Follower' => array('className' => 'Follower','order' => 'Follower.rank'));}
class Follower extends AppModel {public $name = 'Follower';}Now, in the LeadersController, we can use the find() method in the Leader model to fetch a Leader and its associatedfollowers. As you can see above, the association array in the Leader model defines a “Leader hasMany Followers”
relationship. For demonstration purposes, let’s use unbindModel() to remove that association in a controller action:
public function some_action() {// This fetches Leaders, and their associated Followers$this->Leader->find('all');// Let's remove the hasMany...
$this->Leader->unbindModel(
array('hasMany' => array('Follower')));// Now using a find function will return// Leaders, with no Followers$this->Leader->find('all');// NOTE: unbindModel only affects the very next// find function. An additional find call will use// the configured association information.
// We've already used find('all') after unbindModel(),// so this will fetch Leaders with associated// Followers once again...
$this->Leader->find('all');}212

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Note: Removing or adding associations using bind- and unbindModel() only works for the next find operation unlessthe second parameter has been set to false. If the second parameter has been set to false, the bind remains in place forthe remainder of the request.
Here’s the basic usage pattern for unbindModel():$this->Model->unbindModel(
array('associationType' => array('associatedModelClassName')));Now that we’ve successfully removed an association on the fly, let’s add one. Our as-of-yet unprincipled Leader needssome associated Principles. The model file for our Principle model is bare, except for the public $name statement. Let’s
associate some Principles to our Leader on the fly (but remember, only for the following find operation). This function
appears in the LeadersController:
public function another_action() {// There is no Leader hasMany Principles in// the leader.php model file, so a find here// only fetches Leaders.
$this->Leader->find('all');// Let's use bindModel() to add a new association// to the Leader model:$this->Leader->bindModel(
array('hasMany' => array('Principle' => array('className' => 'Principle'))));// If we need keep this association after model reset// we will pass a second boolean parameter like this:$this->Leader->bindModel(
array('hasMany' => array('Principle' => array('className' => 'Principle'
)
)
),
false
);
// Now that we're associated correctly,// we can use a single find function to fetch// Leaders with their associated principles:$this->Leader->find('all');}
There you have it. The basic usage for bindModel() is the encapsulation of a normal association array inside an array
whose key is named after the type of association you are trying to create:

More on models

213

CakePHP Cookbook Documentation, Release 2.x$this->Model->bindModel(
array('associationName' => array('associatedModelClassName' => array(// normal association keys go here...
))));Even though the newly bound model doesn’t need any sort of association definition in its model file, it will still need
to be correctly keyed in order for the new association to work properly.
Multiple relations to the same model
There are cases where a Model has more than one relation to another Model. For example, you might have a Message
model that has two relations to the User model: one relation to the user who sends a message, and a second to theuser who receives the message. The messages table will have a field user_id, but also a field recipient_id. Now your
Message model can look something like:class Message extends AppModel {public $belongsTo = array('Sender' => array('className' => 'User','foreignKey' => 'user_id'),'Recipient' => array('className' => 'User','foreignKey' => 'recipient_id'));}
Recipient is an alias for the User model. Now let’s see what the User model would look like:class User extends AppModel {public $hasMany = array('MessageSent' => array('className' => 'Message','foreignKey' => 'user_id'),'MessageReceived' => array('className' => 'Message','foreignKey' => 'recipient_id'));}
It is also possible to create self associations as shown below:
class Post extends AppModel {public $belongsTo = array('Parent' => array(214(continues on next page)

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'className' => 'Post','foreignKey' => 'parent_id'));
public $hasMany = array('Children' => array('className' => 'Post','foreignKey' => 'parent_id'));}
Fetching a nested array of associated records:
If your table has a parent_id field, you can also use find(‘threaded’) to fetch a nested array of records using a single
query without setting up any associations.
Joining tables
In SQL, you can combine related tables using the JOIN statement. This allows you to perform complex searches acrossmultiple tables (for example, search posts given several tags).
In CakePHP, some associations (belongsTo and hasOne) perform automatic joins to retrieve data, so you can issue
queries to retrieve models based on data in the related one.
But this is not the case with hasMany and hasAndBelongsToMany associations. Here is where forcing joins comes tothe rescue. You only have to define the necessary joins to combine tables and get the desired results for your query.
Note: Remember that you need to set the recursion to -1 for this to work: $this->Channel->recursive = -1;
To force a join between tables, you need to use the “modern” syntax for Model::find(), adding a ‘joins’ key to the$options array. For example:$options['joins'] = array(
array('table' => 'channels','alias' => 'Channel','type' => 'LEFT','conditions' => array('Channel.id = Item.channel_id',)));$Item->find('all', $options);

Note: Note that the ‘joins’ arrays are not keyed.
In the above example, a model called Item is left-joined to the channels table. You can alias the table with the Model
name, so the retrieved data complies with the CakePHP data structure.
The keys that define the join are the following:More on models

215

CakePHP Cookbook Documentation, Release 2.x• table: The table for the join.
• alias: An alias to the table. The name of the model associated with the table is the best bet.
• type: The type of join: inner, left or right.
• conditions: The conditions to perform the join.
With joins, you could add conditions based on Related model fields:$options['joins'] = array(
array('table' => 'channels','alias' => 'Channel','type' => 'LEFT','conditions' => array('Channel.id = Item.channel_id',)));$options['conditions'] = array('Channel.private' => 1);
$privateItems = $Item->find('all', $options);
You could perform several joins as needed in hasAndBelongsToMany:
Suppose there is a Book hasAndBelongsToMany Tag association. This relation uses a books_tags table as a join table,
so you need to join the books table to the books_tags table, and this with the tags table:$options['joins'] = array(
array('table' => 'books_tags','alias' => 'BooksTag','type' => 'inner','conditions' => array('Book.id = BooksTag.book_id')),
array('table' => 'tags','alias' => 'Tag','type' => 'inner','conditions' => array('BooksTag.tag_id = Tag.id')));$options['conditions'] = array('Tag.tag' => 'Novel');$books = $Book->find('all', $options);
Using joins allows you to have maximum flexibility in how CakePHP handles associations and fetches the data. However, in most cases, you can use other tools to achieve the same results such as correctly defining associations, binding

216

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

models on the fly and using the Containable behavior. This feature should be used with care because it could lead, in a
few cases, into ill-formed SQL queries if combined with any of the former techniques described for associating models.

Retrieving Your Data
As stated before, one of the roles of the Model layer is to get data from multiple types of storage. The CakePHP Modelclass comes with some functions that will help you search for this data, sort it, paginate it, and filter it. The mostcommon function you will use in models is Model::find()findfind(string $type = 'first', array $params = array())
Find is the multifunctional workhorse of all model data-retrieval functions. $type can be 'all', 'first', 'count','list', 'neighbors' or 'threaded', or any custom finder you can define. Keep in mind that $type is casesensitive. Using an upper case character (for example, All) will not produce the expected results.
$params is used to pass all parameters to the various types of find(), and has the following possible keys by default,
all of which are optional:
array('conditions' => array('Model.field' => $thisValue), // array of conditions'recursive' => 1, // int// array of field names'fields' => array('Model.field1', 'DISTINCT Model.field2'),// string or array defining order'order' => array('Model.created', 'Model.field3 DESC'),'group' => array('Model.field'), // fields to GROUP BY'limit' => n, // int'page' => n, // int'offset' => n, // int'callbacks' => true, // other possible values are false, 'before', 'after''having' => array('COUNT(Model.field) >' => 1), // array of HAVING conditions'lock' => true, // Enable FOR UPDATE locking)
It’s also possible to add and use other parameters. Some types of find() and behaviors make use of this ability, andyour own model methods can, too.
If your find() operation fails to match any records, you will get an empty array.
New in version 2.10.0: The having and lock options were added.
find(‘first’)find('first', $params) will return one result. You’d use this for any case where you expect only one result. Below
are a couple of simple (controller code) examples:
public function some_function() {// ...
$semiRandomArticle = $this->Article->find('first');$lastCreated = $this->Article->find('first', array('order' => array('Article.created' => 'desc')(continues on next page)

More on models

217

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)));$specificallyThisOne = $this->Article->find('first', array('conditions' => array('Article.id' => 1)));// ...
}In the first example, no parameters at all are passed to find, so no conditions or sort order will be used. The format
returned from find('first') call is of the form:
Array([ModelName] => Array([id] => 83[field1] => value1[field2] => value2[field3] => value3)[AssociatedModelName] => Array([id] => 1[field1] => value1[field2] => value2[field3] => value3))

find(‘count’)find('count', $params) returns an integer value. Below are a couple of simple (controller code) examples:
public function some_function() {// ...
$total = $this->Article->find('count');$pending = $this->Article->find('count', array('conditions' => array('Article.status' => 'pending')));$authors = $this->Article->User->find('count');$publishedAuthors = $this->Article->find('count', array('fields' => 'DISTINCT Article.user_id','conditions' => array('Article.status !=' => 'pending')));// ...
}

Note: Don’t pass fields as an array to find('count'). You would only need to specify fields for a DISTINCT
count (since otherwise, the count is always the same, dictated by the conditions).

218

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

find(‘all’)find('all', $params) returns an array of potentially multiple results. It is, in fact, the mechanism used by allfind() variants, as well as paginate. Below are a couple of simple (controller code) examples:
public function some_function() {// ...
$allArticles = $this->Article->find('all');$pending = $this->Article->find('all', array('conditions' => array('Article.status' => 'pending')));$allAuthors = $this->Article->User->find('all');$allPublishedAuthors = $this->Article->User->find('all', array('conditions' => array('Article.status !=' => 'pending')));// ...
}

Note: In the above example, $allAuthors will contain every user in the users table. There will be no condition
applied to the find, since none were passed.
The results of a call to find('all') will be of the following form:
Array([0] => Array([ModelName] => Array([id] => 83[field1] => value1[field2] => value2[field3] => value3)[AssociatedModelName] => Array([id] => 1[field1] => value1[field2] => value2[field3] => value3)))

More on models

219CakePHP Cookbook Documentation, Release 2.x

find(‘list’)find('list', $params) returns an indexed array, useful for any place where you would want a list, such as for
populating input select boxes. Below are a couple of simple (controller code) examples:
public function some_function() {// ...
$allArticles = $this->Article->find('list');$pending = $this->Article->find('list', array('conditions' => array('Article.status' => 'pending')));$allAuthors = $this->Article->User->find('list');$allPublishedAuthors = $this->Article->find('list', array('fields' => array('User.id', 'User.name'),'conditions' => array('Article.status !=' => 'pending'),'recursive' => 0));// ...
}

Note: In the above example, $allAuthors will contain every user in the users table. There will be no condition
applied to the find, since none were passed.
The results of a call to find('list') will be in the following form:
Array(//[id] => 'displayValue',[1] => 'displayValue1',[2] => 'displayValue2',[4] => 'displayValue4',[5] => 'displayValue5',[6] => 'displayValue6',[3] => 'displayValue3',)
When calling find('list'), the fields passed are used to determine what should be used as the array key andvalue, and optionally what to group the results by. By default, the primary key for the model is used for the key, andthe display field (which can be configured using the model attribute displayField) is used for the value. Some further
examples to clarify:
public function some_function() {// ...
$justusernames = $this->Article->User->find('list', array('fields' => array('User.username')));$usernameMap = $this->Article->User->find('list', array('fields' => array('User.username', 'User.first_name')));$usernameGroups = $this->Article->User->find('list', array('fields' => array('User.username', 'User.first_name', 'User.group')));(continues on next page)

220

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// ...
}
With the above code example, the resultant vars would look something like this:$justusernames = Array(//[id] => 'username',[213] => 'AD7six',[25] => '_psychic_',[1] => 'PHPNut',[2] => 'gwoo',[400] => 'jperras',)$usernameMap = Array(//[username] => 'firstname',['AD7six'] => 'Andy',['_psychic_'] => 'John',['PHPNut'] => 'Larry',['gwoo'] => 'Gwoo',['jperras'] => 'Joël',)$usernameGroups = Array(['User'] => Array(['PHPNut'] => 'Larry',['gwoo'] => 'Gwoo',)['Admin'] => Array(['_psychic_'] => 'John',['AD7six'] => 'Andy',['jperras'] => 'Joël',))More on models

221CakePHP Cookbook Documentation, Release 2.x

find(‘threaded’)find('threaded', $params) returns a nested array, and is appropriate if you want to use the parent_id field of
your model data to build nested results. Below are a couple of simple (controller code) examples:
public function some_function() {// ...
$allCategories = $this->Category->find('threaded');$comments = $this->Comment->find('threaded', array('conditions' => array('article_id' => 50)));// ...
}

Tip: A better way to deal with nested data is using the Tree behavior
In the above code example, $allCategories will contain a nested array representing the whole category structure.
The results of a call to find('threaded') will be of the following form:
Array([0] => Array([ModelName] => Array([id] => 83[parent_id] => null[field1] => value1[field2] => value2[field3] => value3)[AssociatedModelName] => Array([id] => 1[field1] => value1[field2] => value2[field3] => value3)[children] => Array([0] => Array([ModelName] => Array([id] => 42[parent_id] => 83[field1] => value1[field2] => value2[field3] => value3)(continues on next page)

222

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[AssociatedModelName] => Array([id] => 2[field1] => value1[field2] => value2[field3] => value3)[children] => Array
(
)
)
...
)
)
)
The order in which results appear can be changed, as it is influenced by the order of processing. For example, if'order' => 'name ASC' is passed in the params to find('threaded'), the results will appear in name order. Anyorder can be used; there is no built-in requirement of this method for the top result to be returned first.
Warning: If you specify fields, you need to always include the id and parent_id (or their current aliases):public function some_function() {$categories = $this->Category->find('threaded', array('fields' => array('id', 'name', 'parent_id')));}
Otherwise, the returned array will not be of the expected nested structure from above.

find(‘neighbors’)find('neighbors', $params) will perform a find similar to ‘first’, but will return the row before and after the one
you request. Below is a simple (controller code) example:
public function some_function() {$neighbors = $this->Article->find('neighbors',array('field' => 'id', 'value' => 3));}
You can see in this example the two required elements of the $params array: field and value. Other elements are stillallowed as with any other find. (For example: If your model acts as containable, then you can specify ‘contain’ in$params.) The result returned from a find('neighbors') call is in the form:
Array([prev] => Array(continues on next page)

More on models

223

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)([ModelName] => Array([id] => 2[field1] => value1[field2] => value2...
)[AssociatedModelName] => Array([id] => 151[field1] => value1[field2] => value2...
))[next] => Array([ModelName] => Array([id] => 4[field1] => value1[field2] => value2...
)[AssociatedModelName] => Array([id] => 122[field1] => value1[field2] => value2
...
)
)
)

Note: Note how the result always contains only two root elements: prev and next. This function does not honor amodel’s default recursive var. The recursive setting must be passed in the parameters on each call.

Creating custom find types
The find method is flexible enough to accept your custom finders. This is done by declaring your own types in a model
variable and by implementing a special function in your model class.
A Model Find Type is a shortcut to find() options. For example, the following two finds are equivalent$this->User->find('first');$this->User->find('all', array('limit' => 1));
The following are core find types:• first224

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x• all• count• list• threaded• neighbors
But what about other types? Let’s say you want a finder for all published articles in your database. The first change
you need to do is add your type to the Model::$findMethods variable in the model
class Article extends AppModel {public $findMethods = array('available' =>}true);

Basically this is just telling CakePHP to accept the value available as the first argument of the find function. The
next step is to implement the function _findAvailable. This is done by convention. If you wanted to implement afinder called myFancySearch, then the method to implement would be named _findMyFancySearch.
class Article extends AppModel {public $findMethods = array('available' =>true);

protected function _findAvailable($state, $query, $results = array()) {if ($state === 'before') {$query['conditions']['Article.published'] = true;
return $query;}
return $results;}}
This all comes together in the following example (controller code):class ArticlesController extends AppController {// Will find all published articles and order them by the created column
public function index() {$articles = $this->Article->find('available', array('order' => array('created' => 'desc')));}}
The special _find[Type] methods receive three arguments as shown above. The first one means the state of the query
execution, which could be either before or after. It is done this way because this function is just a sort of callback
function that has the ability to modify the query before it is done, or to modify the results after they are fetched.
Typically the first thing to check in our custom find function is the state of the query. The before state is the moment
to modify the query, bind new associations, apply more behaviors, and interpret any special key that is passed in thesecond argument of find. This state requires you to return the $query argument (modified or not).
The after state is the perfect place to inspect the results, inject new data, process it in order to return it in anotherformat, or do whatever you like to the recently fetched data. This state requires you to return the $results array(modified or not).

More on models

225

CakePHP Cookbook Documentation, Release 2.x

You can create as many custom finders as you like, and they are a great way of reusing code in your application acrossmodels.
It is also possible to paginate via a custom find type using the ‘findType’ option as follows:class ArticlesController extends AppController {// Will paginate all published articles
public function index() {$this->paginate = array('findType' => 'available');$articles = $this->paginate();$this->set(compact('articles'));}}Setting the $this->paginate property as above on the controller will result in the type of the find becoming
available, and will also allow you to continue to modify the find results.
To simply return the count of a custom find type, call count like you normally would, but pass in the find type in anarray for the second argument.
class ArticlesController extends AppController {// Will find the count of all published articles (using the available find defined␣
above)
public function index() {$count = $this->Article->find('count', array('type' => 'available'
));
}

˓→

}
If your pagination page count is becoming corrupt, it may be necessary to add the following code to your AppModel,
which should fix the pagination count:class AppModel extends Model {/*** Removes 'fields' key from count query on custom finds when it is an array,* as it will completely break the Model::_findCount() call*
* @param string $state Either "before" or "after"* @param array $query* @param array $results* @return int The number of records found, or false* @access protected* @see Model::find()*/protected function _findCount($state, $query, $results = array()) {if ($state === 'before') {if (isset($query['type']) &&isset($this->findMethods[$query['type']])) {$query = $this->{'_find' . ucfirst($query['type'])(continues on next page)

226

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}('before', $query);if (!empty($query['fields']) && is_array($query['fields'])) {if (!preg_match('/^count/i', current($query['fields']))) {unset($query['fields']);}}}}return parent::_findCount($state, $query, $results);}}?>
Changed in version 2.2.
You no longer need to override _findCount for fixing incorrect count results. The 'before' state of your custom
finder will now be called again with $query['operation'] = 'count'. The returned $query will be used in_findCount() If necessary, you can distinguish by checking the 'operation' key and return a different $query:protected function _findAvailable($state, $query, $results = array()) {if ($state === 'before') {$query['conditions']['Article.published'] = true;
if (!empty($query['operation']) && $query['operation'] === 'count') {return $query;}$query['joins'] = array(//array of required joins);
return $query;}
return $results;}

Magic Find Types
These magic functions can be used as a shortcut to search your tables by a certain field. Just add the name of the field(in CamelCase format) to the end of these functions, and supply the criteria for that field as the first parameter.
findAllBy() functions will return results in a format like find('all'), while findBy() return in the same format
as find('first')
findAllBy
findAllBy<fieldName>(string $value, array $fields, array $order, int $limit, int $page,
int $recursive)

More on models

227

CakePHP Cookbook Documentation, Release 2.x

findAllBy<x> Example

Corresponding SQL Fragment$this->Product->findAllByOrderStatus('3');$this->Recipe->findAllByType('Cookie');$this->User->findAllByLastName('Anderson');$this->Cake->findAllById(7);$this->User->findAllByEmailOrUsername('jhon','jhon');$this->User->findAllByUsernameAndPassword('jhon','123');$this->User->findAllByLastName('psychic', array(),array('User.user_name => 'asc'));Product.order_status = 3
Recipe.type = 'Cookie'
User.last_name = 'Anderson'
Cake.id = 7
User.email = 'jhon' OR User.
username = 'jhon';User.username = 'jhon' AND User.
password = '123';
User.last_name = 'psychic' ORDER
BY User.user_name ASC

The returned result is an array formatted just as it would be from find('all').
Custom Magic Finders
As of 2.8, you can use any custom finder method with the magic method interface. For example, if your model implements a published finder, you can use those finders with the magic findBy method:$results = $this->Article->findPublishedByAuthorId(5);// Is equivalent to$this->Article->find('published', array('conditions' => array('Article.author_id' => 5)));
New in version 2.8.0: Custom magic finders were added in 2.8.0.
findByfindBy<fieldName>(string $value);
The findBy magic functions also accept some optional parameters:
findBy<fieldName>(string $value[, mixed $fields[, mixed $order]]);findBy<x> Example

Corresponding SQL Fragment$this->Product->findByOrderStatus('3');
Product.order_status = 3$this->Recipe->findByType('Cookie');
Recipe.type = 'Cookie'$this->User->findByLastName('Anderson');
User.last_name = 'Anderson';$this->User->findByEmailOrUsername('jhon',User.email = 'jhon' OR User.username ='jhon');'jhon';$this->User->findByUsernameAndPassword('jhon', User.username = 'jhon' AND User.password'123');= '123';$this->Cake->findById(7);Cake.id = 7
findBy() functions return results like find('first')

228

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Model::query()query(string $query)
SQL calls that you can’t or don’t want to make via other model methods can be made using the model’s query()method (though this should only rarely be necessary).
If you use this method, be sure to properly escape all parameters using the value() method on the database driver.
Failing to escape parameters will create SQL injection vulnerabilities.
Note: query() does not honor $Model->cacheQueries as its functionality is inherently disjoint from that of the calling model. To avoid caching calls to query, supply a second argument of false, ie: query($query, $cachequeries= false)query() uses the table name in the query as the array key for the returned data, rather than the model name. For
example:$this->Picture->query("SELECT * FROM pictures LIMIT 2;");might return:Array([0] => Array([pictures] => Array([id] => 1304
[user_id] => 759))[1] => Array([pictures] => Array([id] => 1305
[user_id] => 759)))
To use the model name as the array key, and get a result consistent with that returned by the Find methods, the query
can be rewritten:$this->Picture->query("SELECT * FROM pictures AS Picture LIMIT 2;");
which returns:
Array([0] => Array([Picture] => Array(continues on next page)

More on models

229CakePHP Cookbook Documentation, Release 2.x(continued from previous page)([id] => 1304
[user_id] => 759))[1] => Array([Picture] => Array([id] => 1305
[user_id] => 759)))

Note: This syntax and the corresponding array structure is valid for MySQL only. CakePHP does not provide any
data abstraction when running queries manually, so exact results will vary between databases.

Model::field()field(string $name, array $conditions = null, string $order = null)
Returns the value of a single field, specified as $name, from the first record matched by $conditions as ordered by$order. If no conditions are passed and the model id is set, it will return the field value for the current model result.
If no matching record is found, it returns false.
$this->Post->id = 22;
echo $this->Post->field('name'); // echo the name for row id 22// echo the name of the last created instanceecho $this->Post->field('name',
array('created <' => date('Y-m-d H:i:s')),'created DESC');

Model::read()read($fields, $id)
read() is a method used to set the current model data (Model::$data)–such as during edits–but it can also be usedin other circumstances to retrieve a single record from the database.
$fields is used to pass a single field name, as a string, or an array of field names; if left empty, all fields will befetched.
$id specifies the ID of the record to be read. By default, the currently selected record, as specified by Model::$id, isused. Passing a different value to $id will cause that record to be selected.
read() always returns an array (even if only a single field name is requested). Use field to retrieve the value of asingle field.
230

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Warning: As the read method overwrites any information stored in the data and id property of the model, you
should be very careful when using this function in general, especially using it in the model callback functions suchas beforeValidate and beforeSave. Generally the find function provides a more robust and easy to work with
API than the read method.

Complex Find Conditions
Most of the model’s find calls involve passing sets of conditions in one way or another. In general, CakePHP prefers
using arrays for expressing any conditions that need to be put after the WHERE clause in any SQL query.
Using arrays is clearer and easier to read, and also makes it very easy to build queries. This syntax also breaks out
the elements of your query (fields, values, operators, etc.) into discrete, manipulatable parts. This allows CakePHP togenerate the most efficient query possible, ensure proper SQL syntax, and properly escape each individual part of thequery. Using associated array syntax also enables CakePHP to secure your queries against any SQL injection attack.
Warning: CakePHP only escapes the associated array values. You should never put user data into the keys, or
put user data into numerically indexexed arrays. Doing so will make you vulnerable to SQL injections.
At its most basic, an array-based query looks like this:$conditions = array("Post.title" => "This is a post", "Post.author_id" => 1);// Example usage with a model:$this->Post->find('first', array('conditions' => $conditions));
The structure here is fairly self-explanatory: it will find any post where the title equals “This is a post” and the author id
is equal to 1. Note that we could have used just “title” as the field name, but when building queries, it is good practiceto always specify the model name, as it improves the clarity of the code, and helps prevent collisions in the future,should you choose to change your schema.
What about other types of matches? These are equally simple. Let’s say we wanted to find all the posts where the title
is not “This is a post”:
array("Post.title !=" => "This is a post")
Notice the '!=' that follows the field name. CakePHP can parse out any valid SQL comparison operator, including
match expressions using LIKE, BETWEEN, or REGEX, as long as you leave a space between field name and the operator.
The one exception here is IN (. . . )-style matches. Let’s say you wanted to find posts where the title was in a given setof values:
array("Post.title" => array("First post", "Second post", "Third post"))
To do a NOT IN (. . . ) match to find posts where the title is not in the given set of values, do the following:array("NOT" => array("Post.title" => array("First post", "Second post", "Third post")))
Adding additional filters to the conditions is as simple as adding additional key/value pairs to the array:

More on models

231

CakePHP Cookbook Documentation, Release 2.x

array (
"Post.title" => array("First post", "Second post", "Third post"),"Post.created >" => date('Y-m-d', strtotime("-2 weeks")))
You can also create finds that compare two fields in the database:array("Post.created = Post.modified")
The above example will return posts where the created date is equal to the modified date (that is, it will return posts
that have never been modified).
Remember that if you find yourself unable to form a WHERE clause in this method (for example, boolean operations),
you can always specify it as a string like:array('Model.field & 8 = 1',// other conditions as usual)
By default, CakePHP joins multiple conditions with boolean AND. This means the snippet below would only match
posts that have been created in the past two weeks, and have a title that matches one in the given set. However, we could
just as easily find posts that match either condition:array("OR" => array("Post.title" => array("First post", "Second post", "Third post"),"Post.created >" => date('Y-m-d', strtotime("-2 weeks"))))
CakePHP accepts all valid SQL boolean operations, including AND, OR, NOT, XOR, etc., and they can be upper or lower
case, whichever you prefer. These conditions are also infinitely nestable. Let’s say you had a belongsTo relationshipbetween Posts and Authors. Let’s say you wanted to find all the posts that contained a certain keyword (“magic”) or
were created in the past two weeks, but you wanted to restrict your search to posts written by Bob:array("Author.name" => "Bob","OR" => array("Post.title LIKE" => "%magic%","Post.created >" => date('Y-m-d', strtotime("-2 weeks"))))
If you need to set multiple conditions on the same field, like when you want to do a LIKE search with multiple terms,
you can do so by using conditions similar to:array('OR' => array(
array('Post.title LIKE' => '%one%'),array('Post.title LIKE' => '%two%')))
The wildcard operators ILIKE and RLIKE (RLIKE since version 2.6) are also available.
CakePHP can also check for null fields. In this example, the query will return records where the post title is not null:

232

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

array("NOT" => array("Post.title" => null))
To handle BETWEEN queries, you can use the following:array('Post.read_count BETWEEN ? AND ?' => array(1, 10))

Note: CakePHP will quote the numeric values depending on the field type in your DB.
How about GROUP BY?:
array('fields' => array('Product.type','MIN(Product.price) as price'),'group' => 'Product.type')The data returned for this would be in the following format:
Array([0] => Array([Product] => Array([type] => Clothing)[0] => Array([price] => 32))[1] => Array...
A quick example of doing a DISTINCT query. You can use other operators, such as MIN(), MAX(), etc., in a similar
fashion:array('fields' => array('DISTINCT (User.name) AS my_column_name'),'order' =>array('User.id DESC'))
You can create very complex conditions by nesting multiple condition arrays:
array('OR' => array(
array('Company.name' => 'Future Holdings'),(continues on next page)

More on models

233

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)array('Company.city' => 'CA')),'AND' => array(
array('OR' => array(
array('Company.status' => 'active'),'NOT' => array(
array('Company.status' => array('inactive', 'suspended'))
)
)
)
)
)
which produces the following SQL:
SELECT `Company`.`id`, `Company`.`name`,`Company`.`description`, `Company`.`location`,`Company`.`created`, `Company`.`status`, `Company`.`size`
FROM`companies` AS `Company`
WHERE((`Company`.`name` = 'Future Holdings')
OR(`Company`.`city` = 'CA'))AND((`Company`.`status` = 'active')
OR (NOT (`Company`.`status` IN ('inactive', 'suspended'))))

Sub-queries
For this example, imagine that we have a “users” table with “id”, “name” and “status”. The status can be “A”, “B” or“C”. We want to retrieve all the users that have status other than “B” using a sub-query.
In order to achieve that, we are going to get the model data source and ask it to build the query as if we were calling afind() method, but it will just return the SQL statement. After that we make an expression and add it to the conditions
array:$conditionsSubQuery['User2.status'] = 'B';
$db = $this->User->getDataSource();$subQuery = $db->buildStatement(
array('fields'=> array('User2.id'),'table'=> $db->fullTableName($this->User),'alias'=> 'User2','limit'=> null,'offset'=> null,'joins'=> array(),'conditions' => $conditionsSubQuery,(continues on next page)

234

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'order''group'=> null,=> null),$this->User);$subQuery = 'User.id NOT IN (' . $subQuery . ') ';$subQueryExpression = $db->expression($subQuery);$conditions[] = $subQueryExpression;$this->User->find('all', compact('conditions'));This should generate the following SQL:
SELECT
User.id AS "User__id",
User.name AS "User__name",
User.status AS "User__status"
FROM
users AS User
WHERE
User.id NOT IN (SELECT
User2.id
FROM
users AS User2
WHERE"User2.status" = 'B')
Also, if you need to pass just part of your query as raw SQL as above, datasource expressions with raw SQL work forany part of the find query.
Prepared Statements
Should you need even more control over your queries, you can make use of prepared statements. This allows you totalk directly to the database driver and send any custom query you like:$db = $this->getDataSource();$db->fetchAll('SELECT * from users where username = ? AND password = ?',array('jhon', '12345'));$db->fetchAll('SELECT * from users where username = :username AND password = :password',array('username' => 'jhon','password' => '12345'));

More on models

235

CakePHP Cookbook Documentation, Release 2.x

Saving Your Data
CakePHP makes saving model data a snap. Data ready to be saved should be passed to the model’s save() method
using the following basic format:
Array([ModelName] => Array([fieldname1] => 'value'[fieldname2] => 'value'))
Most of the time you won’t even need to worry about this format: CakePHP’s FormHelper, and model findmethods all package data in this format. If you’re using FormHelper, the data is also conveniently available in$this->request->data for quick usage.
Here’s a quick example of a controller action that uses a CakePHP model to save data to a database table:
public function edit($id) {// Has any form data been POSTed?
if ($this->request->is('post')) {// If the form data can be validated and saved...
if ($this->Recipe->save($this->request->data)) {// Set a session flash message and redirect.
$this->Session->setFlash('Recipe Saved!');return $this->redirect('/recipes');}}// If no form data, find the recipe to be edited// and hand it to the view.
$this->set('recipe', $this->Recipe->findById($id));}
When save is called, the data passed to it in the first parameter is validated using CakePHP’s validation mechanism(see Data Validation chapter for more information). If for some reason your data isn’t saving, be sure to check to see ifsome validation rules are being broken. You can debug this situation by outputting Model::$validationErrors:
if ($this->Recipe->save($this->request->data)) {// handle the success.
}debug($this->Recipe->validationErrors);
There are a few other save-related methods in the model that you’ll find useful:236

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Model::set($one, $two = null)
Model::set() can be used to set one or many fields of data to the data array inside a model. This is useful when using
models with the ActiveRecord features offered by Model:$this->Post->read(null, 1);$this->Post->set('title', 'New title for the article');$this->Post->save();
Is an example of how you can use set() to update single fields, in an ActiveRecord approach. You can also use set()to assign new values to multiple fields:$this->Post->read(null, 1);$this->Post->set(array('title' => 'New title','published' => false));$this->Post->save();
The above would update the title and published fields and save the record to the database.
Model::clear()This method can be used to reset model state and clear out any unsaved data and validation errors.
New in version 2.4.
Model::save(array $data = null, boolean $validate = true, array $fieldList = array())
Featured above, this method saves array-formatted data. The second parameter allows you to sidestep validation, andthe third allows you to supply a list of model fields to be saved. For added security, you can limit the saved fields to thoselisted in $fieldList. When using a fieldList the primary key will be included in the fieldList automatically.
Note: If $fieldList is not supplied, a malicious user can add additional fields to the form data (if you are not using
SecurityComponent), and by this change fields that were not originally intended to be changed.
The save method also has an alternate syntax:
save(array $data = null, array $params = array())$params array can have any of the following available options as keys:• validate Set to true/false to enable/disable validation.
• fieldList An array of fields you want to allow for saving.
• callbacks Set to false to disable callbacks. Using ‘before’ or ‘after’ will enable only those callbacks.
• counterCache (since 2.4) Boolean to control updating of counter caches (if any)
• atomic (since 2.6) Boolean to indicate you want records saved in a transaction.
More information about model callbacks is available here

More on models

237CakePHP Cookbook Documentation, Release 2.x

Tip: If you don’t want the modified field to be automatically updated when saving some data add 'modified' =>false to your $data array
Once a save has been completed, the ID for the object can be found in the $id attribute of the model object - something
especially handy when creating new objects.
$this->Ingredient->save($newData);$newIngredientId = $this->Ingredient->id;
Creating or updating is controlled by the model’s id field. If $Model->id is set, the record with this primary key is
updated. Otherwise a new record is created:// Create: id isn't set or is null
$this->Recipe->create();$this->Recipe->save($this->request->data);// Update: id is set to a numerical value$this->Recipe->id = 2;
$this->Recipe->save($this->request->data);

Tip: When calling save in a loop, don’t forget to call clear().
If you want to update a value, rather than create a new one, make sure you are passing the primary key field into thedata array:$data = array('id' => 10, 'title' => 'My new title');// This will update Recipe with id 10$this->Recipe->save($data);

Model::create(array $data = array())
This method resets the model state for saving new information. It does not actually create a record in the database but
clears Model::$id and sets Model::$data based on your database field defaults. If you have not defined defaults for yourdatabase fields, Model::$data will be set to an empty array.
If the $data parameter (using the array format outlined above) is passed, it will be merged with the database field
defaults and the model instance will be ready to save with that data (accessible at $this->data).
If false or null are passed for the $data parameter, Model::$data will be set to an empty array.
Tip: If you want to insert a new row instead of updating an existing one you should always call create() first. Thisavoids conflicts with possible prior save calls in callbacks or other places.

238

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Model::saveField(string $fieldName, string $fieldValue, $validate = false)
Used to save a single field value. Set the ID of the model ($this->ModelName->id = $id) just before calling
saveField(). When using this method, $fieldName should only contain the name of the field, not the name of
the model and field.
For example, to update the title of a blog post, the call to saveField from a controller might look something like this:$this->Post->saveField('title', 'A New Title for a New Day');

Warning: You can’t stop the modified field being updated with this method, you need to use the save() method.
The saveField method also has an alternate syntax:
saveField(string $fieldName, string $fieldValue, array $params = array())$params array can have any of the following available options as keys:• validate Set to true/false to enable/disable validation.
• callbacks Set to false to disable callbacks. Using ‘before’ or ‘after’ will enable only those callbacks.
• counterCache (since 2.4) Boolean to control updating of counter caches (if any)
Model::updateAll(array $fields, mixed $conditions)
Updates one or more records in a single call. Fields to be updated, along with their values, are identified by the $fields
array. Records to be updated are identified by the $conditions array. If $conditions argument is not supplied or it
is set to true, all records will be updated.
For example, to approve all bakers who have been members for over a year, the update call might look something like:$thisYear = date('Y-m-d H:i:s', strtotime('-1 year'));$this->Baker->updateAll(array('Baker.approved' => true),
array('Baker.created <=' => $thisYear));
The $fields array accepts SQL expressions. Literal values should be quoted manually using DboSource::value().
For example if one of your model methods was calling updateAll() you would do the following:$db = $this->getDataSource();$value = $db->value($value, 'string');$this->updateAll(array('Baker.status' => $value),
array('Baker.status' => 'old'));

Note: Even if the modified field exists for the model being updated, it is not going to be updated automatically by the
ORM. Just add it manually to the array if you need it to be updated.
For example, to close all tickets that belong to a certain customer:

More on models

239CakePHP Cookbook Documentation, Release 2.x$this->Ticket->updateAll(array('Ticket.status' => "'closed'"),array('Ticket.customer_id' => 453));
By default, updateAll() will automatically join any belongsTo association for databases that support joins. To prevent
this, temporarily unbind the associations.
Model::saveMany(array $data = null, array $options = array())
Method used to save multiple rows of the same model at once. The following options may be used:• validate: Set to false to disable validation, true to validate each record before saving, ‘first’ to validate allrecords before any are saved (default),• atomic: If true (default), will attempt to save all records in a single transaction. Should be set to false if
database/table does not support transactions.
• fieldList: Equivalent to the $fieldList parameter in Model::save()• deep: (since 2.1) If set to true, also associated data is saved; see also saveAssociated()• callbacks Set to false to disable callbacks. Using ‘before’ or ‘after’ will enable only those callbacks.
• counterCache (since 2.4) Boolean to control updating of counter caches (if any)
For saving multiple records of single model, $data needs to be a numerically indexed array of records like this:$data = array(
array('title' => 'title 1'),array('title' => 'title 2'),);

Note: Note that we are passing numerical indexes instead of usual $data containing the Article key. When saving
multiple records of same model the records arrays should be just numerically indexed without the model key.
It is also acceptable to have the data in the following format:$data = array(
array('Article' => array('title' => 'title 1')),array('Article' => array('title' => 'title 2')),);
To save also associated data with $options['deep'] = true (since 2.1), the two above examples would look like:$data = array(
array('title' => 'title 1', 'Assoc' => array('field' => 'value')),array('title' => 'title 2'),);
$data = array(
array('Article' => array('title' => 'title 1'),
'Assoc' => array('field' => 'value')),(continues on next page)

240

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)array('Article' => array('title' => 'title 2')),);$Model->saveMany($data, array('deep' => true));
Keep in mind that if you want to update a record instead of creating a new one you just need to add the primary key
index to the data row:$data = array(
array(// This creates a new row'Article' => array('title' => 'New article')),array(// This updates an existing row'Article' => array('id' => 2, 'title' => 'title 2')),);Model::saveAssociated(array $data = null, array $options = array())
Method used to save multiple model associations at once. The following options may be used:• validate: Set to false to disable validation, true to validate each record before saving, ‘first’ to validate allrecords before any are saved (default),• atomic: If true (default), will attempt to save all records in a single transaction. Should be set to false if
database/table does not support transactions.
• fieldList: Equivalent to the $fieldList parameter in Model::save()• deep: (since 2.1) If set to true, not only directly associated data is saved, but deeper nested associated data as
well. Defaults to false.
• counterCache (since 2.4) Boolean to control updating of counter caches (if any)
For saving a record along with its related record having a hasOne or belongsTo association, the data array should be
like this:$data = array('User' => array('username' => 'billy'),'Profile' => array('sex' => 'Male', 'occupation' => 'Programmer'),);
For saving a record along with its related records having hasMany association, the data array should be like this:$data = array('Article' => array('title' => 'My first article'),'Comment' => array(
array('body' => 'Comment 1', 'user_id' => 1),
array('body' => 'Comment 2', 'user_id' => 12),array('body' => 'Comment 3', 'user_id' => 40),),);
And for saving a record along with its related records having hasMany with more than two levels deep associations,the data array should be as follow:

More on models

241CakePHP Cookbook Documentation, Release 2.x$data = array('User' => array('email' => 'john-doe@cakephp.org'),'Cart' => array(
array('payment_status_id' => 2,'total_cost' => 250,
'CartItem' => array(
array('cart_product_id' => 3,'quantity' => 1,'cost' => 100,),array('cart_product_id' => 5,'quantity' => 1,'cost' => 150,
)
)
)
)
);

Note:
If successful, the foreign key of the main model will be stored in the related models’ id field, i.e.
$this->RelatedModel->id.
For saving a record along with its related records having hasMany association and deeper associated Comment belongsTo User data as well, the data array should be like this:$data = array('Article' => array('title' => 'My first article'),'Comment' => array(
array('body' => 'Comment 1', 'user_id' => 1),
array('body' => 'Save a new user as well','User' => array('first' => 'mad', 'last' => 'coder')),),);And save this data with:$Article->saveAssociated($data, array('deep' => true));

Warning: Be careful when checking saveAssociated calls with atomic option set to false. It returns an array
instead of boolean.
Example of using fieldList with multiple models:$this->SomeModel->saveAll($data, array('fieldList' => array((continues on next page)242

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'SomeModel' => array('field_1'),'AssociatedModel' => array('field_2', 'field_3'))));
The fieldList will be an array of model aliases as keys and arrays with fields as values. The model names are not nested
like in the data to be saved.
Changed in version 2.1: Model::saveAll() and friends now support passing the fieldList for multiple models.
You can now save deeper associated data as well with setting $options['deep'] = true;
Model::saveAll(array $data = null, array $options = array())
The saveAll function is just a wrapper around the saveMany and saveAssociated methods. it will inspect the data
and determine what type of save it should perform. If data is formatted in a numerical indexed array, saveMany will
be called, otherwise saveAssociated is used.
This function receives the same options as the former two, and is generally a backwards compatible function. It is
recommended using either saveMany or saveAssociated depending on the case.
Saving Related Model Data (hasOne, hasMany, belongsTo)
When working with associated models, it is important to realize that saving model data should always be done by thecorresponding CakePHP model. If you are saving a new Post and its associated Comments, then you would use both
Post and Comment models during the save operation.
If neither of the associated model records exists in the system yet (for example, you want to save a new User and theirrelated Profile records at the same time), you’ll need to first save the primary, or parent model.
To get an idea of how this works, let’s imagine that we have an action in our UsersController that handles the savingof a new User and a related Profile. The example action shown below will assume that you’ve POSTed enough data
(using the FormHelper) to create a single User and a single Profile:public function add() {if (!empty($this->request->data)) {// We can save the User data:// it should be in $this->request->data['User']$user = $this->User->save($this->request->data);// If the user was saved, Now we add this information to the data// and save the Profile.
if (!empty($user)) {// The ID of the newly created user has been set// as $this->User->id.
$this->request->data['Profile']['user_id'] = $this->User->id;// Because our User hasOne Profile, we can access// the Profile model through the User model:$this->User->Profile->save($this->request->data);}(continues on next page)

More on models

243CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}}As a rule, when working with hasOne, hasMany, and belongsTo associations, it’s all about keying. The basic idea is to
get the key from one model and place it in the foreign key field on the other. Sometimes this might involve using the$id attribute of the model class after a save(), but other times it might just involve gathering the ID from a hidden
input on a form that’s just been POSTed to a controller action.
To supplement the basic approach used above, CakePHP also offers a very handy method saveAssociated(), whichallows you to validate and save multiple models in one shot. In addition, saveAssociated() provides transactional
support to ensure data integrity in your database (i.e. if one model fails to save, the other models will not be saved
either).
Note: For transactions to work correctly in MySQL your tables must use InnoDB engine. Remember that MyISAM
tables do not support transactions.
Let’s see how we can use saveAssociated() to save Company and Account models at the same time.
First, you need to build your form for both Company and Account models (we’ll assume that Company hasMany
Account):
echo $this->Form->create('Company', array('action' => 'add'));echo $this->Form->input('Company.name', array('label' => 'Company name'));echo $this->Form->input('Company.description');echo $this->Form->input('Company.location');echo $this->Form->input('Account.0.name', array('label' => 'Account name'));echo $this->Form->input('Account.0.username');echo $this->Form->input('Account.0.email');echo $this->Form->end('Add');
Take a look at the way we named the form fields for the Account model. If Company is our main model,
saveAssociated() will expect the related model’s (Account) data to arrive in a specific format. And having
Account.0.fieldName is exactly what we need.
Note: The above field naming is required for a hasMany association. If the association between the models is hasOne,
you have to use ModelName.fieldName notation for the associated model.
Now, in our CompaniesController we can create an add() action:
public function add() {if (!empty($this->request->data)) {// Use the following to avoid validation errors:
unset($this->Company->Account->validate['company_id']);$this->Company->saveAssociated($this->request->data);}}
That’s all there is to it. Now our Company and Account models will be validated and saved all at the same time. Bydefault saveAssociated will validate all values passed and then try to perform a save for each.

244

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Saving hasMany through data
Let’s see how data stored in a join table for two models is saved. As shown in the hasMany through (The Join Model)
section, the join table is associated to each model using a hasMany type of relationship. Our example involves the Headof Cake School asking us to write an application that allows him to log a student’s attendance on a course with days
attended and grade. Take a look at the following code.
// Controller/CourseMembershipController.php
class CourseMembershipsController extends AppController {public $uses = array('CourseMembership');
public function index() {$this->set('courseMembershipsList',$this->CourseMembership->find('all'));}public function add() {if ($this->request->is('post')) {if ($this->CourseMembership->saveAssociated($this->request->data)) {return $this->redirect(array('action' => 'index'));}}}}// View/CourseMemberships/add.ctp<?php echo $this->Form->create('CourseMembership'); ?><?php echo $this->Form->input('Student.first_name'); ?><?php echo $this->Form->input('Student.last_name'); ?><?php echo $this->Form->input('Course.name'); ?><?php echo $this->Form->input('CourseMembership.days_attended'); ?><?php echo $this->Form->input('CourseMembership.grade'); ?><button type="submit">Save</button><?php echo $this->Form->end(); ?>The data array will look like this when submitted.
Array([Student] => Array([first_name] => Joe[last_name] => Bloggs)[Course] => Array([name] => Cake)
[CourseMembership] => Array

More on models(continues on next page)245

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)([days_attended] => 5
[grade] => A))
CakePHP will happily be able to save the lot together and assign the foreign keys of the Student and Course into
CourseMembership with a saveAssociated call with this data structure. If we run the index action of our CourseMembershipsController the data structure received now from a find(‘all’) is:Array([0] => Array([CourseMembership] => Array([id] => 1[student_id] => 1
[course_id] => 1
[days_attended] => 5
[grade] => A)
[Student] => Array([id] => 1[first_name] => Joe[last_name] => Bloggs)[Course] => Array([id] => 1[name] => Cake)))
There are of course many ways to work with a join model. The version above assumes you want to save everything atonce. There will be cases where you want to create the Student and Course independently and at a later point associate
the two together with a CourseMembership. So you might have a form that allows selection of existing students andcourses from pick lists or ID entry and then the two meta-fields for the CourseMembership, e.g.
// View/CourseMemberships/add.ctp<?php echo $this->Form->create('CourseMembership'); ?><?php
echo $this->Form->input('Student.id',array('type' => 'text',(continues on next page)

246

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'label' => 'Student ID','default' => 1
)
);
?>
<?php
echo $this->Form->input('Course.id',array('type' => 'text','label' => 'Course ID','default' => 1));?><?php echo $this->Form->input('CourseMembership.days_attended'); ?><?php echo $this->Form->input('CourseMembership.grade'); ?><button type="submit">Save</button><?php echo $this->Form->end(); ?>And the resultant POST:
Array([Student] => Array([id] => 1)[Course] => Array([id] => 1)[CourseMembership] => Array([days_attended] => 10[grade] => 5))
Again CakePHP is good to us and pulls the Student id and Course id into the CourseMembership with the saveAssociated.

More on models

247CakePHP Cookbook Documentation, Release 2.x

Saving Related Model Data (HABTM)
Saving models that are associated by hasOne, belongsTo, and hasMany is pretty simple: you just populate the foreign
key field with the ID of the associated model. Once that’s done, you just call the save() method on the model, andeverything gets linked up correctly. An example of the required format for the data array passed to save() for the Tagmodel is shown below:
Array([Recipe] => Array([id] => 42)[Tag] => Array([name] => Italian))
You can also use this format to save several records and their HABTM associations with saveAll(), using an array
like the following:Array([0] => Array([Recipe] => Array([id] => 42)[Tag] => Array([name] => Italian))[1] => Array([Recipe] => Array([id] => 43)[Tag] => Array([name] => Pasta))[2] => Array([Recipe] => Array([id] => 51)[Tag] => Array(continues on next page)

248

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)([name] => Mexican))[3] => Array([Recipe] => Array([id] => 17)[Tag] => Array([name] => American (new))))
Passing the above array to saveAll() will create the contained tags, each associated with their respective recipes.
Another example that is helpful is when you need to save many Tags to a Post. You need to pass the associated HABTM
data in the following HABTM array format. Note you only need to pass in the id’s of the associated HABTM model
however it needs to be nested again:
Array([0] => Array([Post] => Array([title] => 'Saving HABTM arrays')[Tag] => Array([Tag] => Array(1, 2, 5, 9)))[1] => Array([Post] => Array([title] => 'Dr Who\'s Name is Revealed')[Tag] => Array([Tag] => Array(7, 9, 15, 19)))[2] => Array([Post] => Array([title] => 'I Came, I Saw and I Conquered')(continues on next page)

More on models

249CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[Tag] => Array([Tag] => Array(11, 12, 15, 19)))[3] => Array([Post] => Array([title] => 'Simplicity is the Ultimate Sophistication')[Tag] => Array([Tag] => Array(12, 22, 25, 29))))
Passing the above array to saveAll($data, array('deep' => true)) will populate the posts_tags join table with
the Tag to Post associations.
As an example, we’ll build a form that creates a new tag and generates the proper data array to associate it on the fly
with some recipe.
The simplest form might look something like this (we’ll assume that $recipe_id is already set to something):<?php echo $this->Form->create('Tag'); ?><?php echo $this->Form->input('Recipe.id',array('type' => 'hidden', 'value' => $recipe_id)); ?><?php echo $this->Form->input('Tag.name'); ?><?php echo $this->Form->end('Add Tag'); ?>In this example, you can see the Recipe.id hidden field whose value is set to the ID of the recipe we want to link the
tag to.
When the save() method is invoked within the controller, it’ll automatically save the HABTM data to the database:public function add() {// Save the association
if ($this->Tag->save($this->request->data)) {// do something on success}}With the preceding code, our new Tag is created and associated with a Recipe, whose ID was set in$this->request->data['Recipe']['id'].
Other ways we might want to present our associated data can include a select drop down list. The data can be pulledfrom the model using the find('list') method and assigned to a view variable of the model name. An input withthe same name will automatically pull in this data into a <select>:// in the controller:$this->set('tags', $this->Recipe->Tag->find('list'));(continues on next page)

250

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// in the view:$this->Form->input('tags');
A more likely scenario with a HABTM relationship would include a <select> set to allow multiple selections. For
example, a Recipe can have multiple Tags assigned to it. In this case, the data is pulled out of the model the same way,
but the form input is declared slightly different. The tag name is defined using the ModelName convention:// in the controller:$this->set('tags', $this->Recipe->Tag->find('list'));// in the view:$this->Form->input('Tag');
Using the preceding code, a multiple select drop down is created, allowing for multiple choices to automatically besaved to the existing Recipe being added or saved to the database.
Self HABTM
Normally HABTM is used to bring 2 models together but it can also be used with only 1 model, though it requires
some extra attention.
The key is in the model setup the className. Simply adding a Project HABTM Project relation causes issues
saving data. By setting the className to the models name and use the alias as key we avoid those issues.
class Project extends AppModel {public $hasAndBelongsToMany = array('RelatedProject' => array('className'=> 'Project','foreignKey'=> 'projects_a_id','associationForeignKey' => 'projects_b_id',),);}
Creating form elements and saving the data works the same as before but you use the alias instead. This:$this->set('projects', $this->Project->find('list'));$this->Form->input('Project');
Becomes this:$this->set('relatedProjects', $this->Project->find('list'));$this->Form->input('RelatedProject');More on models

251CakePHP Cookbook Documentation, Release 2.x

What to do when HABTM becomes complicated?
By default when saving a HasAndBelongsToMany relationship, CakePHP will delete all rows on the join table before
saving new ones. For example if you have a Club that has 10 Children associated. You then update the Club with 2children. The Club will only have 2 Children, not 12.
Also note that if you want to add more fields to the join (when it was created or meta information) this is possible with
HABTM join tables, but it is important to understand that you have an easy option.
HasAndBelongsToMany between two models is in reality shorthand for three models associated through both a hasMany and a belongsTo association.
Consider this example:
Child hasAndBelongsToMany Club
Another way to look at this is adding a Membership model:Child hasMany Membership
Membership belongsTo Child, ClubClub hasMany Membership.
These two examples are almost the exact same. They use the same amount of named fields in the database and the sameamount of models. The important differences are that the “join” model is named differently and its behavior is morepredictable.
Tip: When your join table contains extra fields besides two foreign keys, you can prevent losing the extra field values
by setting 'unique' array key to 'keepExisting'. You could think of this similar to ‘unique’ => true, but without
losing data from the extra fields during save operation. Additionally, if you used bake in order to create the models,
this is set automatically. See: HABTM association arrays.
However, in most cases it’s easier to make a model for the join table and setup hasMany, belongsTo associations asshown in example above instead of using HABTM association.
Datatables
While CakePHP can have datasources that aren’t database driven, most of the time, they are. CakePHP is designed tobe agnostic and will work with MySQL, Microsoft SQL Server, PostgreSQL and others. You can create your databasetables as you normally would. When you create your Model classes, they’ll automatically map to the tables that you’ve
created. Table names are by convention lowercase and pluralized with multi-word table names separated by underscores. For example, a Model name of Ingredient expects the table name ingredients. A Model name of EventRegistration would expect a table name of event_registrations. CakePHP will inspect your tables to determine the data typeof each field and uses this information to automate various features such as outputting form fields in the view. Field
names are by convention lowercase and separated by underscores.

252

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Using created and modified
By defining a created and/or modified field in your database table as datetime fields (default null), CakePHP willrecognize those fields and populate them automatically whenever a record is created or saved to the database (unlessthe data being saved already contains a value for these fields).
The created and modified fields will be set to the current date and time when the record is initially added. The
modified field will be updated with the current date and time whenever the existing record is saved.
If you have created or modified data in your $this->data (e.g. from a Model::read or Model::set) before a
Model::save() then the values will be taken from $this->data and not automagically updated. If you don’t want that you
can use unset($this->data['Model']['modified']), etc. Alternatively you can override the Model::save() toalways do it for you:class AppModel extends Model {public function save($data = null, $validate = true, $fieldList = array()) {// Clear modified field value before each save$this->set($data);
if (isset($this->data[$this->alias]['modified'])) {unset($this->data[$this->alias]['modified']);}return parent::save($this->data, $validate, $fieldList);}}
If you are saving data with a fieldList and the created and modified fields are not present in the whitelist, thefields will continue to have the values automatically assigned. When included in the fieldList, the created andmodified fields work like any other field.

Deleting Data
CakePHP’s Model class offers a few ways to delete records from your database.
deletedelete(integer $id = null, boolean $cascade = true);
Deletes the record identified by $id. By default, also deletes records dependent on the record specified to be deleted.
For example, when deleting a User record that is tied to many Recipe records (User ‘hasMany’ or ‘hasAndBelongsToMany’ Recipes):• if $cascade is set to true, the related Recipe records are also deleted if the model’s dependent-value is set to true.
• if $cascade is set to false, the Recipe records will remain after the User has been deleted.
If your database supports foreign keys and cascading deletes, it’s often more efficient to rely on that feature than
CakePHP’s cascading. The one benefit to using the cascade feature of Model::delete() is that it allows you toleverage behaviors and model callbacks:$this->Comment->delete($this->request->data('Comment.id'));You can hook custom logic into the delete process using the beforeDelete and afterDelete callbacks present inboth Models and Behaviors. See Callback Methods for more information.
More on models

253

CakePHP Cookbook Documentation, Release 2.x

Note: If you delete a record with dependent records and one of their delete callbacks, e.g. beforeDelete returns
false, it will not stop the further event propagation nor does it change the return value of the initial delete.

deleteAlldeleteAll(mixed $conditions, $cascade = true, $callbacks = false)deleteAll() is similar to delete(), except that deleteAll() will delete all records that match the supplied conditions. The $conditions array should be supplied as a SQL fragment or array.
• conditions Conditions to match• cascade Boolean, Set to true to delete records that depend on this record• callbacks Boolean, Run callbacks
Return boolean True on success, false on failure.
Example:// Delete with array conditions similar to find()$this->Comment->deleteAll(array('Comment.spam' => true), false);
If you delete with either callbacks and/or cascade, rows will be found and then deleted. This will often result in morequeries being issued. Associations will be reset before the matched records are deleted in deleteAll(). If you use
bindModel() or unbindModel() to change the associations, you should set reset to false.
Note: deleteAll() will return true even if no records are deleted, as the conditions for the delete query were successful
and no matching records remain.

Data Validation
Data validation is an important part of any application, as it helps to make sure that the data in a Model conforms to thebusiness rules of the application. For example, you might want to make sure that passwords are at least eight characterslong, or ensure that usernames are unique. Defining validation rules makes form handling much, much easier.
There are many different aspects to the validation process. What we’ll cover in this section is the model side of things.
Essentially: what happens when you call the save() method of your model. For more information about how to handle
the displaying of validation errors, check out FormHelper.
The first step to data validation is creating the validation rules in the Model. To do that, use the Model::validate arrayin the Model definition, for example:
class User extends AppModel {public $validate = array();}In the example above, the $validate array is added to the User Model, but the array contains no validation rules.
Assuming that the users table has login, email and born fields, the example below shows some simple validation rulesthat apply to those fields:

254

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

class User extends AppModel {public $validate = array('login' => 'alphaNumeric','email' => 'email','born' => 'date');}This last example shows how validation rules can be added to model fields. For the login field, only letters and numbers will be accepted, the email should be valid, and born should be a valid date. Defining validation rules enables
CakePHP’s automagic showing of error messages in forms if the data submitted does not follow the defined rules.
CakePHP has many validation rules and using them can be quite easy. Some of the built-in rules allow you to verify
the formatting of emails, URLs, and credit card numbers – we’ll cover these in detail later on.
Here is a more complex validation example that takes advantage of some of these built-in validation rules:class User extends AppModel {public $validate = array('login' => array('alphaNumeric' => array('rule' => 'alphaNumeric','required' => true,'message' => 'Letters and numbers only'),'between' => array('rule' => array('lengthBetween', 5, 15),
'message' => 'Between 5 to 15 characters')),'password' => array('rule' => array('minLength', '8'),'message' => 'Minimum 8 characters long'),'email' => 'email','born' => array('rule' => 'date','message' => 'Enter a valid date','allowEmpty' => true));}
Two validation rules are defined for login: it should contain letters and numbers only, and its length should be between
5 and 15. The password field should be a minimum of 8 characters long. The email should be a valid email address,
and born should be a valid date. Also, notice how you can define specific error messages that CakePHP will use whenthese validation rules fail.
As the example above shows, a single field can have multiple validation rules. And if the built-in rules do not matchyour criteria, you can always add your own validation rules as required.
Now that you’ve seen the big picture on how validation works, let’s look at how these rules are defined in the model.
There are three different ways that you can define validation rules: simple arrays, single rule per field, and multiple
rules per field.

More on models

255

CakePHP Cookbook Documentation, Release 2.x

Simple Rules
As the name suggests, this is the simplest way to define a validation rule. The general syntax for defining rules this way
is:public $validate = array('fieldName' => 'ruleName');
Where, ‘fieldName’ is the name of the field the rule is defined for, and ‘ruleName’ is a pre-defined rule name, such as‘alphaNumeric’, ‘email’ or ‘isUnique’.
For example, to ensure that the user is giving a well formatted email address, you could use this rule:
public $validate = array('user_email' => 'email');

One Rule Per Field
This definition technique allows for better control of how the validation rules work. But before we discuss that, let’s
see the general usage pattern adding a rule for a single field:
public $validate = array('fieldName1' => array(// or: array('ruleName', 'param1', 'param2' ...)
'rule' => 'ruleName','required' => true,'allowEmpty' => false,// or: 'update''on' => 'create','message' => 'Your Error Message'));
The ‘rule’ key is required. If you only set ‘required’ => true, the form validation will not function correctly. This isbecause ‘required’ is not actually a rule.
As you can see here, each field (only one field shown above) is associated with an array that contains five keys: ‘rule’,‘required’, ‘allowEmpty’, ‘on’ and ‘message’. Let’s have a closer look at these keys.
ruleThe ‘rule’ key defines the validation method and takes either a single value or an array. The specified ‘rule’ may be thename of a method in your model, a method of the core Validation class, or a regular expression. For more informationon the rules available by default, see Core Validation Rules.
If the rule does not require any parameters, ‘rule’ can be a single value e.g.
public $validate = array('login' => array('rule' => 'alphaNumeric'));If the rule requires some parameters (like the max, min or range), ‘rule’ should be an array:256

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

public $validate = array('password' => array('rule' => array('minLength', 8)));Remember, the ‘rule’ key is required for array-based rule definitions.
requiredThis key accepts either a boolean, or create or update. Setting this key to true will make the field always required.
While setting it to create or update will make the field required only for update or create operations. If ‘required’is evaluated to true, the field must be present in the data array. For example, if the validation rule has been defined asfollows:
public $validate = array('login' => array('rule' => 'alphaNumeric','required' => true));
The data sent to the model’s save() method must contain data for the login field. If it doesn’t, validation will fail. The
default value for this key is boolean false.
required => true does not mean the same as the validation rule notBlank(). required => true indicates thatthe array key must be present - it does not mean it must have a value. Therefore validation will fail if the field is notpresent in the dataset, but may (depending on the rule) succeed if the value submitted is empty (‘’).
Changed in version 2.1: Support for create and update were added.
allowEmpty
If set to false, the field value must be nonempty, where “nonempty” is defined as !empty($value) ||is_numeric($value). The numeric check is so that CakePHP does the right thing when $value is zero.
The difference between required and allowEmpty can be confusing. 'required' => true means that you cannot
save the model without the key for this field being present in $this->data (the check is performed with isset);
whereas, 'allowEmpty' => false makes sure that the current field value is nonempty, as described above.
on
The ‘on’ key can be set to either one of the following values: ‘update’ or ‘create’. This provides a mechanism thatallows a certain rule to be applied either during the creation of a new record, or during update of a record.
If a rule has defined ‘on’ => ‘create’, the rule will only be enforced during the creation of a new record. Likewise, if it
is defined as ‘on’ => ‘update’, it will only be enforced during the updating of a record.
The default value for ‘on’ is null. When ‘on’ is null, the rule will be enforced during both creation and update.

More on models

257CakePHP Cookbook Documentation, Release 2.x

message
The message key allows you to define a custom validation error message for the rule:
public $validate = array('password' => array('rule' => array('minLength', 8),'message' => 'Password must be at least 8 characters long'));Note: Regardless of the rule, validation failure without a defined message defaults to “This field cannot be left blank.”

Multiple Rules per Field
The technique outlined above gives us much more flexibility than simple rules assignment, but there’s an extra step we
can take in order to gain more fine-grained control of data validation. The next technique we’ll outline allows us toassign multiple validation rules per model field.
If you would like to assign multiple validation rules to a single field, this is basically how it should look:public $validate = array('fieldName' => array('ruleName' => array('rule' => 'ruleName',// extra keys like on, required, etc. go here...
),'ruleName2' => array('rule' => 'ruleName2',// extra keys like on, required, etc. go here...
)));As you can see, this is quite similar to what we did in the previous section. There, for each field we had only one arrayof validation parameters. In this case, each ‘fieldName’ consists of an array of rule indexes. Each ‘ruleName’ contains
a separate array of validation parameters.
This is better explained with a practical example:
public $validate = array('login' => array('loginRule-1' => array('rule' => 'alphaNumeric','message' => 'Only alphabets and numbers allowed',),'loginRule-2' => array('rule' => array('minLength', 8),'message' => 'Minimum length of 8 characters'
)
)
);

258

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

The above example defines two rules for the login field: loginRule-1 and loginRule-2. As you can see, each rule isidentified with an arbitrary name.
When using multiple rules per field the ‘required’ and ‘allowEmpty’ keys need to be used only once in the first rule.
last
In case of multiple rules per field by default if a particular rule fails error message for that rule is returned and thefollowing rules for that field are not processed. If you want validation to continue in spite of a rule failing set key last
to false for that rule.
In the following example even if “rule1” fails “rule2” will be processed and error messages for both failing rules will
be returned if “rule2” also fails:
public $validate = array('login' => array('rule1' => array('rule' => 'alphaNumeric','message' => 'Only alphabets and numbers allowed','last' => false),'rule2' => array('rule' => array('minLength', 8),'message' => 'Minimum length of 8 characters')));
When specifying validation rules in this array form it’s possible to avoid providing the message key. Consider thisexample:public $validate = array('login' => array('Only alphabets and numbers allowed' => array('rule' => 'alphaNumeric',),));
If the alphaNumeric rules fails the array key for this rule ‘Only alphabets and numbers allowed’ will be returned as
error message since the message key is not set.
Custom Validation Rules
If you haven’t found what you need thus far, you can always create your own validation rules. There are two ways you
can do this: by defining custom regular expressions, or by creating custom validation methods.

More on models

259

CakePHP Cookbook Documentation, Release 2.x

Custom Regular Expression Validation
If the validation technique you need to use can be completed by using regular expression matching, you can define acustom expression as a field validation rule:
public $validate = array('login' => array('rule' => '/^[a-z0-9]{3,}$/i','message' => 'Only letters and integers, min 3 characters'));
The example above checks if the login contains only letters and integers, with a minimum of three characters.
The regular expression in the rule must be delimited by slashes. The optional trailing ‘i’ after the last slash means the
reg-exp is case insensitive.
Adding your own Validation Methods
Sometimes checking data with regular expression patterns is not enough. For example, if you want to ensure that a
promotional code can only be used 25 times, you need to add your own validation function, as shown below:
class User extends AppModel {public $validate = array('promotion_code' => array('rule' => array('limitDuplicates', 25),
'message' => 'This code has been used too many times.'));
public function limitDuplicates($check, $limit) {// $check will have value: array('promotion_code' => 'some-value')// $limit will have value: 25$existingPromoCount = $this->find('count', array('conditions' => $check,'recursive' => -1));
return $existingPromoCount < $limit;}}
The current field to be validated is passed into the function as first parameter as an associated array with field name as
key and posted data as value.
If you want to pass extra parameters to your validation function, add elements onto the ‘rule’ array, and handle them as
extra params (after the main $check param) in your function.
Your validation function can be in the model (as in the example above), or in a behavior that the model implements.
This includes mapped methods.
Model/behavior methods are checked first, before looking for a method on the Validation class. This means that you
can override existing validation methods (such as alphaNumeric()) at an application level (by adding the method to
AppModel), or at model level.

260

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

When writing a validation rule which can be used by multiple fields, take care to extract the field value from the $check
array. The $check array is passed with the form field name as its key and the field value as its value. The full record
being validated is stored in $this->data member variable:class Post extends AppModel {public $validate = array('slug' => array('rule' => 'alphaNumericDashUnderscore','message' => 'Slug can only be letters,' .
' numbers, dash and underscore'));public function alphaNumericDashUnderscore($check) {// $data array is passed using the form field name as the key// have to extract the value to make the function generic$value = array_values($check);$value = $value[0];return preg_match('|^[0-9a-zA-Z_-]*$|', $value);}}Note: Your own validation methods must have public visibility. Validation methods that are protected andprivate are not supported.
The method should return true if the value is valid. If the validation failed, return false. The other valid return value
are strings which will be shown as the error message. Returning a string means the validation failed. The string will
overwrite the message set in the $validate array and be shown in the view’s form as the reason why the field was not
valid.
Dynamically change validation rules
Using the $validate property to declare validation rules is a good way of statically defining rules for each model.
Nevertheless, there are cases when you want to dynamically add, change or remove validation rules from the predefinedset.
All validation rules are stored in a ModelValidator object, which holds every rule set for each field in your model.
Defining new validation rules is as easy as telling this object to store new validation methods for the fields you want to.
Adding new validation rules
New in version 2.2.
The ModelValidator objects allows several ways for adding new fields to the set. The first one is using the add
method:// Inside a model class$this->validator()->add('password', 'required', array('rule' => 'notBlank',(continues on next page)

More on models

261

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'required' => 'create'));This will add a single rule to the password field in the model. You can chain multiple calls to add to create as manyrules as you like:// Inside a model class$this->validator()->add('password', 'required', array('rule' => 'notBlank','required' => 'create'))->add('password', 'size', array('rule' => array('lengthBetween', 8, 20),'message' => 'Password should be at least 8 chars long'));
It is also possible to add multiple rules at once for a single field:$this->validator()->add('password', array('required' => array('rule' => 'notBlank','required' => 'create'),'size' => array('rule' => array('lengthBetween', 8, 20),'message' => 'Password should be at least 8 chars long')));
Alternatively, you can use the validator object to set rules directly to fields using the array interface:$validator = $this->validator();$validator['username'] = array('unique' => array('rule' => 'isUnique','required' => 'create'),'alphanumeric' => array('rule' => 'alphanumeric'
)
);

262

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Modifying current validation rules
New in version 2.2.
Modifying current validation rules is also possible using the validator object, there are several ways in which you canalter current rules, append methods to a field or completely remove a rule from a field rule set:// In a model class$this->validator()->getField('password')->setRule('required', array('rule' => 'required','required' => true));
You can also completely replace all the rules for a field using a similar method:// In a model class$this->validator()->getField('password')->setRules(array('required' => array(...),'otherRule' => array(...)));
If you wish to just modify a single property in a rule you can set properties directly into the CakeValidationRuleobject:// In a model class$this->validator()->getField('password')->getRule('required')->message = 'This field cannot be left blank';
Properties in any CakeValidationRule get their name from the array keys one is allowed to use when defining avalidation rule’s properties, such as the array keys ‘message’ and ‘allowEmpty’ for example.
As with adding new rule to the set, it is also possible to modify existing rules using the array interface:$validator = $this->validator();$validator['username']['unique'] = array('rule' => 'isUnique','required' => 'create');$validator['username']['unique']->last = true;$validator['username']['unique']->message = 'Name already taken';

Removing rules from the set
New in version 2.2.
It is possible to both completely remove all rules for a field and to delete a single rule in a field’s rule set:// Completely remove all rules for a field$this->validator()->remove('username');// Remove 'required' rule from password$this->validator()->remove('password', 'required');Optionally, you can use the array interface to delete rules from the set:More on models

263

CakePHP Cookbook Documentation, Release 2.x$validator = $this->validator();// Completely remove all rules for a field
unset($validator['username']);// Remove 'required' rule from password
unset($validator['password']['required']);Core Validation Rules
class Validation
The Validation class in CakePHP contains many validation rules that can make model data validation much easier. This
class contains many oft-used validation techniques you won’t need to write on your own. Below, you’ll find a complete
list of all the rules, along with usage examples.
static Validation::alphaNumeric(mixed $check)
The data for the field must only contain letters and numbers.
public $validate = array('login' => array('rule' => 'alphaNumeric','message' => 'Usernames must only contain letters and numbers.'));
static Validation::lengthBetween(string $check, integer $min, integer $max)
The length of the data for the field must fall within the specified numeric range. Both minimum and maximum
values must be supplied. Uses = not.
public $validate = array('password' => array('rule' => array('lengthBetween', 5, 15),
'message' => 'Passwords must be between 5 and 15 characters long.'));
The data is checked by number of characters, not number of bytes. If you want to validate against pure ASCII
input instead of UTF-8 compatible, you will have to write your own custom validators.
static Validation::blank(mixed $check)
This rule is used to make sure that the field is left blank or only white space characters are present in its value.
White space characters include space, tab, carriage return, and newline.
public $validate = array('id' => array('rule' => 'blank','on' => 'create'));static Validation::boolean(string $check)
The data for the field must be a boolean value. Valid values are true or false, integers 0 or 1 or strings ‘0’ or ‘1’.

264

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

public $validate = array('myCheckbox' => array('rule' => array('boolean'),'message' => 'Incorrect value for myCheckbox'));
static Validation::cc(mixed $check, mixed $type = 'fast', boolean $deep = false, string $regex = null)This rule is used to check whether the data is a valid credit card number. It takes three parameters: ‘type’, ‘deep’and ‘regex’.
The ‘type’ key can be assigned to the values of ‘fast’, ‘all’ or any of the following:• amex• bankcard• diners• disc• electron• enroute• jcb• maestro• mc• solo• switch• visa• voyager
If ‘type’ is set to ‘fast’, it validates the data against the major credit cards’ numbering formats. Setting ‘type’ to‘all’ will check with all the credit card types. You can also set ‘type’ to an array of the types you wish to match.
The ‘deep’ key should be set to a boolean value. If it is set to true, the validation will check the Luhn algorithmof the credit card (https://en.wikipedia.org/wiki/Luhn_algorithm). It defaults to false.
The ‘regex’ key allows you to supply your own regular expression that will be used to validate the credit card
number:public $validate = array('ccnumber' => array('rule' => array('cc', array('visa', 'maestro'), false, null),'message' => 'The credit card number you supplied was invalid.'));
static Validation::comparison(mixed $check1, string $operator = null, integer $check2 = null)Comparison is used to compare numeric values. It supports “is greater”, “is less”, “greater or equal”, “less orequal”, “equal to”, and “not equal”. Some examples are shown below:

More on models

265CakePHP Cookbook Documentation, Release 2.x

public $validate = array('age' => array('rule' => array('comparison', '>=', 18),
'message' => 'Must be at least 18 years old to qualify.'));
public $validate = array('age' => array('rule' => array('comparison', 'greater or equal', 18),
'message' => 'Must be at least 18 years old to qualify.'));
static Validation::custom(mixed $check, string $regex = null)
Used when a custom regular expression is needed:public $validate = array('infinite' => array('rule' => array('custom', '\u221E'),'message' => 'Please enter an infinite number.'));static Validation::date(string $check, mixed $format = 'ymd', string $regex = null)This rule ensures that data is submitted in valid date formats. A single parameter (which can be an array) can bepassed that will be used to check the format of the supplied date. The value of the parameter can be one of thefollowing:• ‘dmy’ e.g. 27-12-2006 or 27-12-06 (separators can be a space, period, dash, forward slash)• ‘mdy’ e.g. 12-27-2006 or 12-27-06 (separators can be a space, period, dash, forward slash)• ‘ymd’ e.g. 2006-12-27 or 06-12-27 (separators can be a space, period, dash, forward slash)• ‘dMy’ e.g. 27 December 2006 or 27 Dec 2006
• ‘Mdy’ e.g. December 27, 2006 or Dec 27, 2006 (comma is optional)• ‘My’ e.g. (December 2006 or Dec 2006)• ‘my’ e.g. 12/2006 or 12/06 (separators can be a space, period, dash, forward slash)• ‘ym’ e.g. 2006/12 or 06/12 (separators can be a space, period, dash, forward slash)• ‘y’ e.g. 2006 (separators can be a space, period, dash, forward slash)
If no keys are supplied, the default key that will be used is ‘ymd’:
public $validate = array('born' => array('rule' => array('date', 'ymd'),'message' => 'Enter a valid date in YY-MM-DD format.','allowEmpty' => true
)
);

266

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

While many data stores require a certain date format, you might consider doing the heavy lifting by accepting awide-array of date formats and trying to convert them, rather than forcing users to supply a given format. The
more work you can do for your users, the better.
Changed in version 2.4: The ym and y formats were added.
static Validation::datetime(array $check, mixed $dateFormat = 'ymd', string $regex = null)This rule ensures that the data is a valid datetime format. A parameter (which can be an array) can be passed to
specify the format of the date. The value of the parameter can be one or more of the following:• ‘dmy’ e.g. 27-12-2006 or 27-12-06 (separators can be a space, period, dash, forward slash)• ‘mdy’ e.g. 12-27-2006 or 12-27-06 (separators can be a space, period, dash, forward slash)• ‘ymd’ e.g. 2006-12-27 or 06-12-27 (separators can be a space, period, dash, forward slash)• ‘dMy’ e.g. 27 December 2006 or 27 Dec 2006
• ‘Mdy’ e.g. December 27, 2006 or Dec 27, 2006 (comma is optional)• ‘My’ e.g. (December 2006 or Dec 2006)• ‘my’ e.g. 12/2006 or 12/06 (separators can be a space, period, dash, forward slash)
If no keys are supplied, the default key that will be used is ‘ymd’:
public $validate = array('birthday' => array('rule' => array('datetime', 'dmy'),'message' => 'Please enter a valid date and time.'));
Also a second parameter can be passed to specify a custom regular expression. If this parameter is used, this willbe the only validation that will occur.
Note that unlike date(), datetime() will validate a date and a time.
static Validation::decimal(string $check, integer $places = null, string $regex = null)This rule ensures that the data is a valid decimal number. A parameter can be passed to specify the number ofdigits required after the decimal point. If no parameter is passed, the data will be validated as a scientific float,
which will cause validation to fail if no digits are found after the decimal point:
public $validate = array('price' => array('rule' => array('decimal', 2)));
static Validation::email(string $check, boolean $deep = false, string $regex = null)This checks whether the data is a valid email address. Passing a boolean true as the second parameter for thisrule will also attempt to verify that the host for the address is valid:public $validate = array('email' => array('rule' => 'email'));public $validate = array('email' => array('rule' => array('email', true),'message' => 'Please supply a valid email address.'(continues on next page)

More on models

267CakePHP Cookbook Documentation, Release 2.x(continued from previous page)));
static Validation::equalTo(mixed $check, mixed $compareTo)
This rule will ensure that the value is equal to, and of the same type as the given value.
public $validate = array('food' => array('rule' => array('equalTo', 'cake'),'message' => 'This value must be the string cake'));static Validation::extension(mixed $check, array $extensions = array('gif', 'jpeg', 'png', 'jpg'))
This rule checks for valid file extensions like .jpg or .png. Allow multiple extensions by passing them in arrayform.
public $validate = array('image' => array('rule' => array('extension',array('gif', 'jpeg', 'png', 'jpg')),'message' => 'Please supply a valid image.'));static Validation::fileSize($check, $operator = null, $size = null)This rule allows you to check filesizes. You can use $operator to decide the type of comparison you want touse. All the operators supported by comparison() are supported here as well. This method will automaticallyhandle array values from $_FILES by reading from the tmp_name key if $check is an array and contains thatkey:
public $validate = array('image' => array('rule' => array('fileSize', '<=', '1MB'),'message' => 'Image must be less than 1MB'));
New in version 2.3: This method was added in 2.3
static Validation::inList(string $check, array $list, boolean $caseInsensitive = false)
This rule will ensure that the value is in a given set. It needs an array of values. The field is valid if the field’svalue matches one of the values in the given array.
Example:public $validate = array('function' => array('allowedChoice' => array('rule' => array('inList', array('Foo', 'Bar')),'message' => 'Enter either Foo or Bar.'(continues on next page)

268

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page))));
Comparison is case sensitive by default. You can set $caseInsensitive to true if you need case insensitive
comparison.
static Validation::ip(string $check, string $type = 'both')This rule will ensure that a valid IPv4 or IPv6 address has been submitted. Accepts as option ‘both’ (default),‘IPv4’ or ‘IPv6’.
public $validate = array('clientip' => array('rule' => array('ip', 'IPv4'), // or 'IPv6' or 'both' (default)'message' => 'Please supply a valid IP address.'));
Model::isUnique()The data for the field must be unique, it cannot be used by any other rows:
public $validate = array('login' => array('rule' => 'isUnique','message' => 'This username has already been taken.'));
You can validate that a set of fields are unique by providing multiple fields and set $or to false:
public $validate = array('email' => array('rule' => array('isUnique', array('email', 'username'), false),'message' => 'This username & email combination has already been used.'));
Make sure to include the original field in the list of fields when making a unique rule across multiple fields.
If a listed field isn’t included in the model data, then it’s treated as a null value. You may consider marking thelisted fields as required.
static Validation::luhn(string|array $check, boolean $deep = false)
The Luhn algorithm: A checksum formula to validate a variety of identification numbers. See https://en.
wikipedia.org/wiki/Luhn_algorithm for more information.
static Validation::maxLength(string $check, integer $max)
This rule ensures that the data stays within a maximum length requirement:
public $validate = array('login' => array('rule' => array('maxLength', 15),
'message' => 'Usernames must be no larger than 15 characters long.'(continues on next page)

More on models

269CakePHP Cookbook Documentation, Release 2.x(continued from previous page)));
This will ensure that the ‘login’ field is less than or equal to 15 characters, not 15 bytes.
static Validation::maxLengthBytes(string $check, integer $max)
This rule ensures that the data stays within a maximum length requirement:
public $validate = array('data' => array('rule' => array('maxLengthBytes', 2 ** 24 - 1),'message' => 'Data can not be bigger than 16 MB.'));
This will ensure that the ‘data’ field is less than or equal to 16777215 bytes.
static Validation::mimeType(mixed $check, array|string $mimeTypes)
New in version 2.2.
This rule checks for valid mime types. Comparison is case sensitive.
Changed in version 2.5.
Since 2.5 $mimeTypes can be a regex string.
public $validate = array('image' => array('rule' => array('mimeType', array('image/gif')),'message' => 'Invalid mime type.'),'logo' => array('rule' => array('mimeType', '#image/.+#'),'message' => 'Invalid mime type.'),);
static Validation::minLength(string $check, integer $min)
This rule ensures that the data meets a minimum length requirement:
public $validate = array('login' => array('rule' => array('minLength', 8),'message' => 'Usernames must be at least 8 characters long.'));
The length here is number of characters, not the number of bytes. If you want to validate against pure ASCII
input instead of UTF-8 compatible, you will have to write your own custom validators.
static Validation::minLengthBytes(string $check, integer $min)
This rule ensures that the data meets a minimum length requirement:
public $validate = array('login' => array((continues on next page)

270

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'rule' => array('minLengthBytes', 2 ** 16 - 1),'message' => 'Data can not be smaller than 64KB.'));
The length here is number of bytes.
static Validation::money(string $check, string $symbolPosition = 'left')
This rule will ensure that the value is in a valid monetary amount.
Second parameter defines where symbol is located (left/right).
public $validate = array('salary' => array('rule' => array('money', 'left'),'message' => 'Please supply a valid monetary amount.'));
static Validation::multiple(mixed $check, mixed $options = array(), boolean $caseInsensitive = false)
Use this for validating a multiple select input. It supports parameters “in”, “max” and “min”.
public $validate = array('multiple' => array('rule' => array('multiple', array('in' => array('do', 're', 'mi', 'fa', 'sol', 'la', 'ti'),'min' => 1,'max' => 3)),
'message' => 'Please select one, two or three options'));Comparison is case sensitive by default. You can set $caseInsensitive to true if you need case insensitive
comparison.
static Validation::notEmpty(mixed $check)
Deprecated since version 2.7.
Use notBlank instead.
static Validation::notBlank(mixed $check)
New in version 2.7.
The basic rule to ensure that a field is not empty.
public $validate = array('title' => array('rule' => 'notBlank','message' => 'This field cannot be left blank'));
Do not use this for a multiple select input as it will cause an error. Instead, use “multiple”.

More on models

271

CakePHP Cookbook Documentation, Release 2.x

static Validation::numeric(string $check)
Checks if the data passed is a valid number.
public $validate = array('cars' => array('rule' => 'numeric','message' => 'Please supply the number of cars.'));static Validation::naturalNumber(mixed $check, boolean $allowZero = false)
New in version 2.2.
This rule checks if the data passed is a valid natural number. If $allowZero is set to true, zero is also accepted
as a value.
public $validate = array('wheels' => array('rule' => 'naturalNumber','message' => 'Please supply the number of wheels.'),'airbags' => array('rule' => array('naturalNumber', true),'message' => 'Please supply the number of airbags.'),);
static Validation::phone(mixed $check, string $regex = null, string $country = 'all')
Phone validates US phone numbers. If you want to validate non-US phone numbers, you can provide a regularexpression as the second parameter to cover additional number formats.
public $validate = array('phone' => array('rule' => array('phone', null, 'us')));
static Validation::postal(mixed $check, string $regex = null, string $country = 'us')
Postal is used to validate ZIP codes from the U.S. (us), Canada (ca), U.K (uk), Italy (it), Germany (de) andBelgium (be). For other ZIP code formats, you may provide a regular expression as the second parameter.
public $validate = array('zipcode' => array('rule' => array('postal', null, 'us')));
static Validation::range(string $check, integer $lower = null, integer $upper = null)This rule ensures that the value is in a given range. If no range is supplied, the rule will check to ensure the value
is a legal finite on the current platform.
public $validate = array('number' => array('rule' => array('range', -1, 11),(continues on next page)272

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'message' => 'Please enter a number between -1 and 11'));
The above example will accept any value which is larger than -1 (e.g., -0.99) and less than 11 (e.g., 10.99).
Note: The range lower/upper are not inclusive
static Validation::ssn(mixed $check, string $regex = null, string $country = null)
Ssn validates social security numbers from the U.S. (us), Denmark (dk), and the Netherlands (nl). For othersocial security number formats, you may provide a regular expression.
public $validate = array('ssn' => array('rule' => array('ssn', null, 'us')));
static Validation::time(string $check)
Time validation, determines if the string passed is a valid time. Validates time as 24hr (HH:MM) or am/pm([H]H:MM[a|p]m) Does not allow/validate seconds.
static Validation::uploadError(mixed $check)
New in version 2.2.
This rule checks if a file upload has an error.
public $validate = array('image' => array('rule' => 'uploadError','message' => 'Something went wrong with the upload.'),);
static Validation::url(string $check, boolean $strict = false)
This rule checks for valid URL formats. Supports http(s), ftp(s), file, news, and gopher protocols:
public $validate = array('website' => array('rule' => 'url'));
To ensure that a protocol is in the url, strict mode can be enabled like so:public $validate = array('website' => array('rule' => array('url', true)));
This validation method uses a complex regular expression that can sometimes cause issues with Apache2 on
Windows using mod_php.
More on models

273

CakePHP Cookbook Documentation, Release 2.x

static Validation::userDefined(mixed $check, object $object, string $method, array $args = null)
Runs an user-defined validation.
static Validation::uuid(string $check)
Checks that a value is a valid UUID: https://tools.ietf.org/html/rfc4122
Localized Validation
The validation rules phone() and postal() will pass off any country prefix they do not know how to handle to anotherclass with the appropriate name. For example if you lived in the Netherlands you would create a class like:class NlValidation {public static function phone($check) {// ...
}
public static function postal($check) {// ...
}}
This file could be placed in APP/Validation/ or App/PluginName/Validation/, but must be imported via
App::uses() before attempting to use it. In your model validation you could use your NlValidation class by doing
the following:
public $validate = array('phone_no' => array('rule' => array('phone', null, 'nl')),'postal_code' => array('rule' => array('postal', null, 'nl')),);When your model data is validated, Validation will see that it cannot handle the nl locale and will attempt to delegate
out to NlValidation::postal() and the return of that method will be used as the pass/fail for the validation. Thisapproach allows you to create classes that handle a subset or group of locales, something that a large switch would nothave. The usage of the individual validation methods has not changed, the ability to pass off to another validator has
been added.
Tip: The Localized Plugin already contains a lot of rules ready to use: https://github.com/cakephp/localized Also
feel free to contribute with your localized validation rules.

Validating Data from the Controller
While normally you would just use the save method of the model, there may be times where you wish to validate thedata without saving it. For example, you may wish to display some additional information to the user before actuallysaving the data to the database. Validating data requires a slightly different process than just saving the data.
First, set the data to the model:$this->ModelName->set($this->request->data);
Then, to check if the data validates, use the validates method of the model, which will return true if it validates andfalse if it doesn’t:

274

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

if ($this->ModelName->validates()) {// it validated logic} else {// didn't validate logic$errors = $this->ModelName->validationErrors;}
It may be desirable to validate your model only using a subset of the validations specified in your model. For example
say you had a User model with fields for first_name, last_name, email and password. In this instance when creating or
editing a user you would want to validate all 4 field rules. Yet when a user logs in you would validate just email andpassword rules. To do this you can pass an options array specifying the fields to validate:if ($this->User->validates(array('fieldList' => array('email', 'password')))) {// valid} else {// invalid}
The validates method invokes the invalidFields method which populates the validationErrors property of the model.
The invalidFields method also returns that data as the result:$errors = $this->ModelName->invalidFields(); // contains validationErrors array
The validation errors list is not cleared between successive calls to invalidFields() So if you are validating in aloop and want each set of errors separately don’t use invalidFields(). Instead use validates() and access thevalidationErrors model property.
It is important to note that the data must be set to the model before the data can be validated. This is different fromthe save method which allows the data to be passed in as a parameter. Also, keep in mind that it is not required to callvalidates prior to calling save as save will automatically validate the data before actually saving.
To validate multiple models, the following approach should be used:
if ($this->ModelName->saveAll($this->request->data, array('validate' => 'only'))) {// validates} else {// does not validate}
If you have validated data before save, you can turn off validation to avoid second check:if ($this->ModelName->saveAll($this->request->data, array('validate' => false))) {// saving without validation}

More on models

275

CakePHP Cookbook Documentation, Release 2.x

Callback Methods
If you want to sneak in some logic just before or after a CakePHP model operation, use model callbacks. These functions
can be defined in model classes (including your AppModel) class. Be sure to note the expected return values for eachof these special functions.
When using callback methods you should remember that behavior callbacks are fired before model callbacks are.
beforeFind
beforeFind(array $query)
Called before any find-related operation. The $query passed to this callback contains information about the current
query: conditions, fields, etc.
If you do not wish the find operation to begin (possibly based on a decision relating to the $query options), returnfalse. Otherwise, return the possibly modified $query, or anything you want to get passed to find and its counterparts.
You might use this callback to restrict find operations based on a user’s role, or make caching decisions based on thecurrent load.
afterFind
afterFind(array $results, boolean $primary = false)
Use this callback to modify results that have been returned from a find operation, or to perform any other post-findlogic. The $results parameter passed to this callback contains the returned results from the model’s find operation, i.e.
something like:$results = array(0 => array('ModelName' => array('field1' => 'value1','field2' => 'value2',),),);
The return value for this callback should be the (possibly modified) results for the find operation that triggered thiscallback.
The $primary parameter indicates whether or not the current model was the model that the query originated on orwhether or not this model was queried as an association. If a model is queried as an association the format of $results
can differ; instead of the result you would normally get from a find operation, you may get this:$results = array('field_1' => 'value1','field_2' => 'value2');

Warning: Code expecting $primary to be true will probably get a “Cannot use string offset as an array” fatal
error from PHP if a recursive find is used.
Below is an example of how afterfind can be used for date formatting:

276

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

public function afterFind($results, $primary = false) {foreach ($results as $key => $val) {if (isset($val['Event']['begindate'])) {$results[$key]['Event']['begindate'] = $this->dateFormatAfterFind($val['Event']['begindate']);}}
return $results;}
public function dateFormatAfterFind($dateString) {return date('d-m-Y', strtotime($dateString));}

beforeValidate
beforeValidate(array $options = array())
Use this callback to modify model data before it is validated, or to modify validation rules if required. This function
must also return true, otherwise the current save() execution will abort.
afterValidate
afterValidate()
Called after data has been checked for errors. Use this callback to perform any data cleanup or preparation if needed.
beforeSave
beforeSave(array $options = array())
Place any pre-save logic in this function. This function executes immediately after model data has been successfully
validated, but just before the data is saved. This function should also return true if you want the save operation tocontinue.
This callback is especially handy for any data-massaging logic that needs to happen before your data is stored. If yourstorage engine needs dates in a specific format, access it at $this->data and modify it.
Below is an example of how beforeSave can be used for date conversion. The code in the example is used for an
application with a begindate formatted like YYYY-MM-DD in the database and is displayed like DD-MM-YYYY in
the application. Of course this can be changed very easily. Use the code below in the appropriate model.
public function beforeSave($options = array()) {if (!empty($this->data['Event']['begindate']) &&!empty($this->data['Event']['enddate'])) {$this->data['Event']['begindate'] = $this->dateFormatBeforeSave($this->data['Event']['begindate']);$this->data['Event']['enddate'] = $this->dateFormatBeforeSave($this->data['Event']['enddate'](continues on next page)

More on models

277CakePHP Cookbook Documentation, Release 2.x(continued from previous page));}
return true;}
public function dateFormatBeforeSave($dateString) {return date('Y-m-d', strtotime($dateString));}

Tip: Make sure that beforeSave() returns true, or your save is going to fail.

afterSaveafterSave(boolean $created, array $options = array())
If you have logic you need to be executed just after every save operation, place it in this callback method. The saved
data will be available in $this->data.
The value of $created will be true if a new record was created (rather than an update).
The $options array is the same one passed to Model::save().
beforeDelete
beforeDelete(boolean $cascade = true)
Place any pre-deletion logic in this function. This function should return true if you want the deletion to continue, andfalse if you want to abort.
The value of $cascade will be true if records that depend on this record will also be deleted.
Tip: Make sure that beforeDelete() returns true, or your delete is going to fail.
// using app/Model/ProductCategory.php// In the following example, do not let a product category be deleted if it// still contains products.
// A call of $this->Product->delete($id) from ProductsController.php has set// $this->id .
// Assuming 'ProductCategory hasMany Product', we can access $this->Product// in the model.
public function beforeDelete($cascade = true) {$count = $this->Product->find("count", array("conditions" => array("product_category_id" => $this->id)));
if ($count == 0) {return true;}
return false;}

278

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

afterDelete
afterDelete()
Place any logic that you want to be executed after every deletion in this callback method.
// perhaps after deleting a record from the database, you also want to delete// an associated filepublic function afterDelete() {$file = new File($this->data['SomeModel']['file_path']);$file->delete();}onErroronError()Called if any problems occur.

Behaviors
Model behaviors are a way to organize some of the functionality defined in CakePHP models. They allow us to separate
and reuse logic that creates a type of behavior, and they do this without requiring inheritance. For example creating
tree structures. By providing a simple yet powerful way to enhance models, behaviors allow us to attach functionality
to models by defining a simple class variable. That’s how behaviors allow models to get rid of all the extra weight thatmight not be part of the business contract they are modeling, or that is also needed in different models and can then be
extrapolated.
As an example, consider a model that gives us access to a database table which stores structural information about atree. Removing, adding, and migrating nodes in the tree is not as simple as deleting, inserting, and editing rows in
the table. Many records may need to be updated as things move around. Rather than creating those tree-manipulation
methods on a per model basis (for every model that needs that functionality), we could simply tell our model to use the
TreeBehavior, or in more formal terms, we tell our model to behave as a Tree. This is known as attaching a behavior
to a model. With just one line of code, our CakePHP model takes on a whole new set of methods that allow it to interact
with the underlying structure.
CakePHP already includes behaviors for tree structures, translated content, access control list interaction, not to mention
the community-contributed behaviors already available in the CakePHP Bakery (https://bakery.cakephp.org). In thissection, we’ll cover the basic usage pattern for adding behaviors to models, how to use CakePHP’s built-in behaviors,and how to create our own.
In essence, Behaviors are Mixins61 with callbacks.
There are a number of Behaviors included in CakePHP. To find out more about each one, reference the chapters below:
61 https://en.wikipedia.org/wiki/Mixin

More on models

279CakePHP Cookbook Documentation, Release 2.x

ACLclass AclBehavior
The Acl behavior provides a way to seamlessly integrate a model with your ACL system. It can create both AROs or
ACOs transparently.
To use the new behavior, you can add it to the $actsAs property of your model. When adding it to the actsAs array you
choose to make the related Acl entry an ARO or an ACO. The default is to create ACOs:
class User extends AppModel {public $actsAs = array('Acl' => array('type' => 'requester'));}This would attach the Acl behavior in ARO mode. To join the ACL behavior in ACO mode use:class Post extends AppModel {public $actsAs = array('Acl' => array('type' => 'controlled'));}For User and Group models it is common to have both ACO and ARO nodes, to achieve this use:class User extends AppModel {public $actsAs = array('Acl' => array('type' => 'both'));}You can also attach the behavior on the fly like so:$this->Post->Behaviors->load('Acl', array('type' => 'controlled'));Changed in version 2.1: You can now safely attach AclBehavior to AppModel. Aco, Aro and AclNode now extend
Model instead of AppModel, which would cause an infinite loop. If your application depends on having those models
to extend AppModel for some reason, then copy AclNode to your application and have it extend AppModel again.
Using the AclBehavior
Most of the AclBehavior works transparently on your Model’s afterSave(). However, using it requires that your Model
has a parentNode() method defined. This is used by the AclBehavior to determine parent->child relationships. A
model’s parentNode() method must return null or return a parent Model reference:
public function parentNode() {return null;}If you want to set an ACO or ARO node as the parent for your Model, parentNode() must return the alias of the ACO
or ARO node:
public function parentNode() {return 'root_node';}
A more complete example. Using an example User Model, where User belongsTo Group:

280

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

public function parentNode() {if (!$this->id && empty($this->data)) {return null;}$data = $this->data;
if (empty($this->data)) {$data = $this->read();}if (!$data['User']['group_id']) {return null;}
return array('Group' => array('id' => $data['User']['group_id']));}In the above example the return is an array that looks similar to the results of a model find. It is important to have theid value set or the parentNode relation will fail. The AclBehavior uses this data to construct its tree structure.
node()The AclBehavior also allows you to retrieve the Acl node associated with a model record. After setting $model->id.
You can use $model->node() to retrieve the associated Acl node.
You can also retrieve the Acl Node for any row, by passing in a data array:$this->User->id = 1;
$node = $this->User->node();$user = array('User' => array('id' => 1));$node = $this->User->node($user);Will both return the same Acl Node information.
If you had setup AclBehavior to create both ACO and ARO nodes, you need to specify which node type you want:$this->User->id = 1;
$node = $this->User->node(null, 'Aro');$user = array('User' => array('id' => 1));$node = $this->User->node($user, 'Aro');More on models

281

CakePHP Cookbook Documentation, Release 2.x

Containableclass ContainableBehavior
A new addition to the CakePHP 1.2 core is the ContainableBehavior. This model behavior allows you to filter andlimit model find operations. Using Containable will help you cut down on needless wear and tear on your database,increasing the speed and overall performance of your application. The class will also help you search and filter yourdata for your users in a clean and consistent way.
Containable allows you to streamline and simplify operations on your model bindings. It works by temporarily or
permanently altering the associations of your models. It does this by using the supplied containments to generate aseries of bindModel and unbindModel calls. Since Containable only modifies existing relationships it will not allow
you to restrict results by distant associations. Instead you should refer to Joining tables.
To use the new behavior, you can add it to the $actsAs property of your model:
class Post extends AppModel {public $actsAs = array('Containable');}You can also attach the behavior on the fly:
$this->Post->Behaviors->load('Containable');Using Containable
To see how Containable works, let’s look at a few examples. First, we’ll start off with a find() call on a model named‘Post’. Let’s say that ‘Post’ hasMany ‘Comment’, and ‘Post’ hasAndBelongsToMany ‘Tag’. The amount of data fetchedin a normal find() call is rather extensive:
debug($this->Post->find('all'));[0] => Array([Post] => Array([id] => 1[title] => First article[content] => aaa[created] => 2008-05-18 00:00:00)[Comment] => Array([0] => Array([id] => 1[post_id] => 1
[author] => Daniel[email] => dan@example.com[website] => http://example.com[comment] => First comment[created] => 2008-05-18 00:00:00)[1] => Array(continues on next page)282

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)([id] => 2[post_id] => 1
[author] => Sam[email] => sam@example.net[website] => http://example.net[comment] => Second comment[created] => 2008-05-18 00:00:00))[Tag] => Array([0] => Array([id] => 1[name] => Awesome)[1] => Array([id] => 2[name] => Baking)))[1] => Array([Post] => Array(...
For some interfaces in your application, you may not need that much information from the Post model. One thing theContainableBehavior does is help you cut down on what find() returns.
For example, to get only the post-related information, you can do the following:$this->Post->contain();$this->Post->find('all');
You can also invoke Containable’s magic from inside the find() call:$this->Post->find('all', array('contain' => false));Having done that, you end up with something a lot more concise:[0] => Array([Post] => Array([id] => 1[title] => First article[content] => aaa[created] => 2008-05-18 00:00:00))(continues on next page)

More on models

283

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[1] => Array([Post] => Array([id] => 2[title] => Second article[content] => bbb[created] => 2008-05-19 00:00:00))
This sort of help isn’t new: in fact, you can do that without the ContainableBehavior doing something like this:$this->Post->recursive = -1;$this->Post->find('all');Containable really shines when you have complex associations, and you want to pare down things that sit at the samelevel. The model’s $recursive property is helpful if you want to hack off an entire level of recursion, but not when
you want to pick and choose what to keep at each level. Let’s see how it works by using the contain() method.
The contain method’s first argument accepts the name, or an array of names, of the models to keep in the find operation.
If we wanted to fetch all posts and their related tags (without any comment information), we’d try something like this:$this->Post->contain('Tag');$this->Post->find('all');
Again, we can use the contain key inside a find() call:$this->Post->find('all', array('contain' => 'Tag'));Without Containable, you’d end up needing to use the unbindModel() method of the model, multiple times if you’reparing off multiple models. Containable creates a cleaner way to accomplish this same task.
Containing deeper associations
Containable also goes a step deeper: you can filter the data of the associated models. If you look at the results of theoriginal find() call, notice the author field in the Comment model. If you are interested in the posts and the names of
the comment authors — and nothing else — you could do something like the following:$this->Post->contain('Comment.author');$this->Post->find('all');// or..
$this->Post->find('all', array('contain' => 'Comment.author'));Here, we’ve told Containable to give us our post information, and just the author field of the associated Comment
model. The output of the find call might look something like this:[0] => Array([Post] => Array(continues on next page)284

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)([id] => 1[title] => First article[content] => aaa[created] => 2008-05-18 00:00:00)[Comment] => Array([0] => Array([author] => Daniel[post_id] => 1)
[1] => Array([author] => Sam[post_id] => 1)))
[1] => Array(...
As you can see, the Comment arrays only contain the author field (plus the post_id which is needed by CakePHP tomap the results).
You can also filter the associated Comment data by specifying a condition:$this->Post->contain('Comment.author = "Daniel"');$this->Post->find('all');//or...
$this->Post->find('all', array('contain' => 'Comment.author = "Daniel"'));
This gives us a result that gives us posts with comments authored by Daniel:[0] => Array([Post] => Array([id] => 1[title] => First article[content] => aaa[created] => 2008-05-18 00:00:00)[Comment] => Array([0] => Array([id] => 1[post_id] => 1
[author] => Daniel(continues on next page)

More on models

285CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[email] => dan@example.com[website] => http://example.com[comment] => First comment[created] => 2008-05-18 00:00:00)))
There is an important caveat to using Containable when filtering on a deeper association. In the previous example,
assume you had 3 posts in your database and Daniel had commented on 2 of those posts. The operation $this->Post>find(‘all’, array(‘contain’ => ‘Comment.author = “Daniel”’)); would return ALL 3 posts, not just the 2 posts that
Daniel had commented on. It won’t return all comments however, just comments by Daniel.
[0] => Array([Post] => Array([id] => 1[title] => First article[content] => aaa[created] => 2008-05-18 00:00:00)[Comment] => Array([0] => Array([id] => 1[post_id] => 1
[author] => Daniel[email] => dan@example.com[website] => http://example.com[comment] => First comment[created] => 2008-05-18 00:00:00)))[1] => Array([Post] => Array([id] => 2[title] => Second article[content] => bbb[created] => 2008-05-18 00:00:00)[Comment] => Array())[2] => Array([Post] => Array(continues on next page)

286

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)([id] => 3[title] => Third article[content] => ccc[created] => 2008-05-18 00:00:00)[Comment] => Array([0] => Array([id] => 22[post_id] => 3[author] => Daniel[email] => dan@example.com[website] => http://example.com[comment] => Another comment[created] => 2008-05-18 00:00:00)))
If you want to filter the posts by the comments, so that posts without a comment by Daniel won’t be returned, the easiest
way is to find all the comments by Daniel and contain the Posts.
$this->Comment->find('all', array('conditions' => 'Comment.author = "Daniel"','contain' => 'Post'));
Additional filtering can be performed by supplying the standard find options:$this->Post->find('all', array('contain' => array('Comment' => array('conditions' => array('Comment.author =' => "Daniel"),'order' => 'Comment.created DESC'))));
Here’s an example of using the ContainableBehavior when you’ve got deep and complex model relationships.
Let’s consider the following model associations:User->Profile
User->Account->AccountSummary
User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes
User->Post->Tag
This is how we retrieve the above associations with Containable:$this->User->find('all', array('contain' => array('Profile','Account' => array((continues on next page)

More on models

287CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'AccountSummary'),'Post' => array('PostAttachment' => array('fields' => array('id', 'name'),'PostAttachmentHistory' => array('HistoryNotes' => array('fields' => array('id', 'note')))),'Tag' => array('conditions' => array('Tag.name LIKE' => '%happy%')
)
)
)
));
Keep in mind that contain key is only used once in the main model, you don’t need to use ‘contain’ again for related
models.
Note: When using ‘fields’ and ‘contain’ options - be careful to include all foreign keys that your query directly or
indirectly requires. Please also note that because Containable must to be attached to all models used in containment,
you may consider attaching it to your AppModel.

ContainableBehavior options
The ContainableBehavior has a number of options that can be set when the Behavior is attached to a model. The
settings allow you to fine tune the behavior of Containable and work with other behaviors more easily.
• recursive (boolean, optional) set to true to allow containable to automatically determine the recursiveness levelneeded to fetch specified models, and set the model recursiveness to this level. setting it to false disables thisfeature. The default value is true.
• notices (boolean, optional) issues E_NOTICES for bindings referenced in a containable call that are not valid.
The default value is true.
• autoFields: (boolean, optional) auto-add needed fields to fetch requested bindings. The default value is true.
• order: (string, optional) the order of how the contained elements are sorted.
From the previous example, this is an example of how to force the posts to be ordered by the date when they were last
updated:$this->User->find('all', array('contain' => array('Profile','Post' => array('order' => 'Post.updated DESC'
)
)
));

288

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

You can change ContainableBehavior settings at run time by reattaching the behavior as seen in Behaviors (Using
Behaviors).
ContainableBehavior can sometimes cause issues with other behaviors or queries that use aggregate functions and/or
GROUP BY statements. If you get invalid SQL errors due to mixing of aggregate and non-aggregate fields, try disabling
the autoFields setting.
$this->Post->Behaviors->load('Containable', array('autoFields' => false));Using Containable with pagination
By including the ‘contain’ parameter in the $paginate property it will apply to both the find(‘count’) and the find(‘all’)done on the model.
See the section Using Containable for further details.
Here’s an example of how to contain associations when paginating:$this->paginate['User'] = array('contain' => array('Profile', 'Account'),'order' => 'User.username');$users = $this->paginate('User');

Note: If you contained the associations through the model instead, it will not honor Containable’s recursive option.
So if you set recursive to -1 for example for the model, it won’t work:$this->User->recursive = -1;$this->User->contain(array('Profile', 'Account'));$users = $this->paginate('User');

Translateclass TranslateBehavior
TranslateBehavior is actually quite easy to setup and works out of the box with very little configuration. In this section,
you will learn how to add and setup the behavior to use in any model.
If you are using TranslateBehavior in alongside containable issue, be sure to set the ‘fields’ key for your queries.
Otherwise you could end up with invalid SQL generated.

More on models289CakePHP Cookbook Documentation, Release 2.x

Initializing the i18n Database Tables
You can either use the CakePHP console or you can manually create it. It is advised to use the console for this, because
it might happen that the layout changes in future versions of CakePHP. Sticking to the console will make sure that you
have the correct layout.
./cake i18n
Select [I] which will run the i18n database initialization script. You will be asked if you want to drop any existingand if you want to create it. Answer with yes if you are sure there is no i18n table already, and answer with yes again
to create the table.
Attaching the Translate Behavior to your Models
Add it to your model by using the $actsAs property like in the following example.
class Post extends AppModel {public $actsAs = array('Translate');}This will do nothing yet, because it expects a couple of options before it begins to work. You need to define which
fields of the current model should be tracked in the translation table we’ve created in the first step.
Defining the Fields
You can set the fields by simply extending the 'Translate' value with another array, like so:class Post extends AppModel {public $actsAs = array('Translate' => array('fieldOne', 'fieldTwo', 'and_so_on'));}
After you have done that (for example putting “title” as one of the fields) you already finished the basic setup. Great!
According to our current example the model should now look something like this:class Post extends AppModel {public $actsAs = array('Translate' => array('title'));}When defining fields for TranslateBehavior to translate, be sure to omit those fields from the translated model’s schema.
If you leave the fields in, there can be issues when retrieving data with fallback locales.

290

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Note: If all the fields in your model are translated be sure to add created and modified columns to your table.
CakePHP requires at least one non primary key field before it will save a record.

Loading Translations With Left Joins
When defining the fields that are translated you can also load translations via LEFT JOIN instead of the standard INNER
JOIN. Doing this will allow you to load partially translated records:
class Post extends AppModel {public $actsAs = array('Translate' => array('title','body','joinType' => 'left'));}
New in version 2.10.0: The joinType option was added in 2.10.0
Conclusion
From now on each record update/creation will cause TranslateBehavior to copy the value of “title” to the translationtable (default: i18n) along with the current locale. A locale is the identifier of the language, so to speak.
Reading translated content
By default the TranslateBehavior will automatically fetch and add in data based on the current locale. The current
locale is read from Configure::read('Config.language') which is assigned by the L10n class. You can override
this default on the fly using $Model->locale.
Retrieve translated fields in a specific locale
By setting $Model->locale you can read translations for a specific locale:// Read the spanish locale data.
$this->Post->locale = 'es';$results = $this->Post->find('first', array('conditions' => array('Post.id' => $id)));// $results will contain the spanish translation.

More on models

291CakePHP Cookbook Documentation, Release 2.x

Retrieve all translation records for a fieldIf you want to have all translation records attached to the current model record you simply extend the field array inyour behavior setup as shown below. The naming is completely up to you.
class Post extends AppModel {public $actsAs = array('Translate' => array('title' => 'titleTranslation'));}
With this setup the result of $this->Post->find() should look something like this:Array([Post] => Array([id] => 1[title] => Beispiel Eintrag[body] => lorem ipsum...
[locale] => de_de)[titleTranslation] => Array([0] => Array([id] => 1[locale] => en_us[model] => Post
[foreign_key] => 1
[field] => title[content] => Example entry)[1] => Array([id] => 2[locale] => de_de[model] => Post
[foreign_key] => 1
[field] => title[content] => Beispiel Eintrag)))

Note: The model record contains a virtual field called “locale”. It indicates which locale is used in this result.
Note that only fields of the model you are directly doing `find` on will be translated. Models attached via associations
292

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

won’t be translated because triggering callbacks on associated models is currently not supported.
Using the bindTranslation method
You can also retrieve all translations, only when you need them, using the bindTranslation method
TranslateBehavior::bindTranslation($fields, $reset)$fields is a named-key array of field and association name, where the key is the translatable field and the value is thefake association name.
$this->Post->bindTranslation(array('title' => 'titleTranslation'));// need at least recursive 1 for this to work.
$this->Post->find('all', array('recursive' => 1));
With this setup the result of your find() should look something like this:Array([Post] => Array([id] => 1[title] => Beispiel Eintrag[body] => lorem ipsum...
[locale] => de_de)[titleTranslation] => Array([0] => Array([id] => 1[locale] => en_us[model] => Post
[foreign_key] => 1
[field] => title[content] => Example entry)[1] => Array([id] => 2[locale] => de_de[model] => Post
[foreign_key] => 1
[field] => title[content] => Beispiel Eintrag)))More on models

293CakePHP Cookbook Documentation, Release 2.x

Saving in another language
You can force the model which is using the TranslateBehavior to save in a language other than the one detected.
To tell a model in what language the content is going to be you simply change the value of the $locale property on
the model before you save the data to the database. You can do that either in your controller or you can define it directlyin the model.
Example A: In your controller:class PostsController extends AppController {public function add() {if (!empty($this->request->data)) {// we are going to save the German version$this->Post->locale = 'de_de';$this->Post->create();if ($this->Post->save($this->request->data)) {return $this->redirect(array('action' => 'index'));}}}}Example B: In your model:
class Post extends AppModel {public $actsAs = array('Translate' => array('title'));// Option 1) just define the property directly
public $locale = 'en_us';// Option 2) create a simple method
public function setLanguage($locale) {$this->locale = $locale;}}Multiple Translation Tables
If you expect a lot entries you probably wonder how to deal with a rapidly growing database table. There are two
properties introduced by TranslateBehavior that allow to specify which “Model” to bind as the model containing thetranslations.
These are $translateModel and $translateTable.
Lets say we want to save our translations for all posts in the table “post_i18ns” instead of the default “i18n” table. To
do so you need to setup your model like this:294

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

class Post extends AppModel {public $actsAs = array('Translate' => array('title'));// Use a different model (and table)
public $translateModel = 'PostI18n';}

Note: It is important that you to pluralize the table. It is now a usual model and can be treated as such and thus
comes with the conventions involved. The table schema itself must be identical with the one generated by the CakePHP
console script. To make sure it fits one could just initialize a empty i18n table using the console and rename the table
afterwards.

Create the TranslateModel
For this to work you need to create the actual model file in your models folder. Reason is that there is no property to
set the displayField directly in the model using this behavior yet.
Make sure that you change the $displayField to 'field'.
class PostI18n extends AppModel {public $displayField = 'field'; // important}// filename: PostI18n.php
That’s all it takes. You can also add all other model stuff here like $useTable. But for better consistency we could dothat in the model which actually uses this translation model. This is where the optional $translateTable comes intoplay.
Changing the Table
If you want to change the name of the table you simply define $translateTable in your model, like so:class Post extends AppModel {public $actsAs = array('Translate' => array('title'));// Use a different model
public $translateModel = 'PostI18n';// Use a different table for translateModelpublic $translateTable = 'post_translations';}

More on models

295CakePHP Cookbook Documentation, Release 2.x

Please note that you can’t use $translateTable alone. If you don’t intend to use a custom $translateModel thenleave this property untouched. Reason is that it would break your setup and show you a “Missing Table” message forthe default I18n model which is created in runtime.
Treeclass TreeBehavior
It’s fairly common to want to store hierarchical data in a database table. Examples of such data might be categories
with unlimited subcategories, data related to a multilevel menu system or a literal representation of hierarchy such as
is used to store access control objects with ACL logic.
For small trees of data, or where the data is only a few levels deep it is simple to add a parent_id field to your databasetable and use this to keep track of which item is the parent of what. Bundled with cake however, is a powerful behavior
which allows you to use the benefits of MPTT logic62 without worrying about any of the intricacies of the technique unless you want to ;).
Requirements
To use the tree behavior, your database table needs 3 fields as listed below (all are ints):• parent - default fieldname is parent_id, to store the id of the parent object• left - default fieldname is lft, to store the lft value of the current row.
• right - default fieldname is rght, to store the rght value of the current row.
If you are familiar with MPTT logic you may wonder why a parent field exists - quite simply it’s easier to do certain
tasks if a direct parent link is stored on the database - such as finding direct children.
Note: The parent field must be able to have a NULL value! It might seem to work if you just give the top elementsa parent value of zero, but reordering the tree (and possible other operations) will fail.

Basic Usage
The tree behavior has a lot packed into it, but let’s start with a simple example - create the following database table andput some data in it:
CREATE TABLE categories (id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,parent_id INTEGER(10) DEFAULT NULL,
lft INTEGER(10) DEFAULT NULL,
rght INTEGER(10) DEFAULT NULL,
name VARCHAR(255) DEFAULT '',PRIMARY KEY (id));
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(continues on next page)
62 https://www.sitepoint.com/hierarchical-data-database-2/

296

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)(1, 'My Categories', NULL, 1, 30);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(2, 'Fun', 1, 2, 15);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(3, 'Sport', 2, 3, 8);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(4, 'Surfing', 3, 4, 5);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(5, 'Extreme knitting', 3, 6, 7);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(6, 'Friends', 2, 9, 14);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(7, 'Gerald', 6, 10, 11);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(8, 'Gwendolyn', 6, 12, 13);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(9, 'Work', 1, 16, 29);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(10, 'Reports', 9, 17, 22);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(11, 'Annual', 10, 18, 19);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(12, 'Status', 10, 20, 21);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(13, 'Trips', 9, 23, 28);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(continues on next page)

More on models

297CakePHP Cookbook Documentation, Release 2.x(continued from previous page)(14, 'National', 13, 24, 25);
INSERT INTO
`categories` (`id`, `name`, `parent_id`, `lft`, `rght`)
VALUES(15, 'International', 13, 26, 27);
For the purpose of checking that everything is setup correctly, we can create a test method and output the contents ofour category tree to see what it looks like. With a simple controller:class CategoriesController extends AppController {public function index() {$data = $this->Category->generateTreeList(
null,
null,
null,
'&nbsp;&nbsp;&nbsp;');
debug($data); die;}}and an even simpler model definition:// app/Model/Category.php
class Category extends AppModel {public $actsAs = array('Tree');}
We can check what our category tree data looks like by visiting /categories You should see something like this:• My Categories– Fun∗ Sport· Surfing· Extreme knitting∗ Friends· Gerald· Gwendolyn– Work∗ Reports· Annual· Status∗ Trips· National· International

298

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Adding data
In the previous section, we used existing data and checked that it looked hierarchal via the method generateTreeList.
However, usually you would add your data in exactly the same way as you would for any model. For example:// pseudo controller code
$data['Category']['parent_id'] = 3;
$data['Category']['name'] = 'Skating';$this->Category->save($data);
When using the tree behavior it’s not necessary to do any more than set the parent_id, and the tree behavior will takecare of the rest. If you don’t set the parent_id, the tree behavior will add to the tree making your new addition a new
top level entry:// pseudo controller code
$data = array();$data['Category']['name'] = 'Other People\'s Categories';$this->Category->save($data);
Running the above two code snippets would alter your tree as follows:• My Categories– Fun∗ Sport· Surfing· Extreme knitting· Skating New∗ Friends· Gerald· Gwendolyn– Work∗ Reports· Annual· Status∗ Trips· National· International• Other People’s Categories New

More on models

299CakePHP Cookbook Documentation, Release 2.x

Modifying data
Modifying data is as transparent as adding new data. If you modify something, but do not change the parent_id field the structure of your data will remain unchanged. For example:// pseudo controller code
$this->Category->id = 5; // id of Extreme knitting$this->Category->save(array('name' => 'Extreme fishing'));
The above code did not affect the parent_id field - even if the parent_id is included in the data that is passed to save if
the value doesn’t change, neither does the data structure. Therefore the tree of data would now look like:• My Categories– Fun∗ Sport· Surfing· Extreme fishing Updated· Skating∗ Friends· Gerald· Gwendolyn– Work∗ Reports· Annual· Status∗ Trips· National· International• Other People’s Categories
Moving data around in your tree is also a simple affair. Let’s say that Extreme fishing does not belong under Sport, butinstead should be located under Other People’s Categories. With the following code:// pseudo controller code
$this->Category->id = 5; // id of Extreme fishing$newParentId = $this->Category->field('id',array('name' => 'Other People\'s Categories'));$this->Category->save(array('parent_id' => $newParentId));
As would be expected the structure would be modified to:• My Categories– Fun∗ Sport300

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x· Surfing· Skating∗ Friends· Gerald· Gwendolyn– Work∗ Reports· Annual· Status∗ Trips· National· International• Other People’s Categories– Extreme fishing Moved
Deleting data
The tree behavior provides a number of ways to manage deleting data. To start with the simplest example; let’s say thatthe reports category is no longer useful. To remove it and any children it may have just call delete as you would for any
model. For example with the following code:// pseudo controller code
$this->Category->id = 10;$this->Category->delete();
The category tree would be modified as follows:• My Categories– Fun∗ Sport· Surfing· Skating∗ Friends· Gerald· Gwendolyn– Work∗ Trips· National· International• Other People’s Categories– Extreme fishingMore on models

301

CakePHP Cookbook Documentation, Release 2.x

Querying and using your data
Using and manipulating hierarchical data can be a tricky business. In addition to the core find methods, with the tree
behavior there are a few more tree-orientated permutations at your disposal.
Note: Most tree behavior methods return and rely on data being sorted by the lft field. If you call find() and do
not order by lft, or call a tree behavior method and pass a sort order, you may get undesirable results.
class TreeBehavior
children($id = null, $direct = false, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null)Parameters• $id – The ID of the record to look up• $direct – Set to true to return only the direct descendants• $fields – Single string field name or array of fields to include in the return• $order – SQL string of ORDER BY conditions• $limit – SQL LIMIT statement• $page – for accessing paged results• $recursive – Number of levels deep for recursive associated Models
The children method takes the primary key value (the id) of a row and returns the children, by default in theorder they appear in the tree. The second optional parameter defines whether or not only direct children should
be returned. Using the example data from the previous section:$allChildren = $this->Category->children(1); // a flat array with 11 items// -- or -$this->Category->id = 1;
$allChildren = $this->Category->children(); // a flat array with 11 items// Only return direct children$directChildren = $this->Category->children(1, true); // a flat array with// 2 items

Note: If you want a recursive array use find('threaded')childCount($id = null, $direct = false)
As with the method children, childCount takes the primary key value (the id) of a row and returns how many
children it has. The second optional parameter defines whether or not only direct children are counted. Using
the example data from the previous section:$totalChildren = $this->Category->childCount(1); // will output 11// -- or -$this->Category->id = 1;
$directChildren = $this->Category->childCount(); // will output 11(continues on next page)302

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// Only counts the direct descendants of this category$numChildren = $this->Category->childCount(1, true); // will output 2
generateTreeList($conditions=null, $keyPath=null, $valuePath=null, $spacer= '_', $recursive=null)Parameters• $conditions – Uses the same conditional options as find().
• $keyPath – Path to the field to use for the key, i.e. “{n}.Post.id”.
• $valuePath – Path to the field to use for the label, i.e. “{n}.Post.title”.
• $spacer – The string to use in front of each item to indicate depth.
• $recursive – The number of levels deep to fetch associated records
This method will return data similar to find(‘list’) but with a nested prefix that is specified in the spacer option
to show the structure of your data. Below is an example of what you can expect this method to return:$treelist = $this->Category->generateTreeList();
Output:array([1] => "My Categories",[2] => "_Fun",[3] => "__Sport",[4] => "___Surfing",[16] => "___Skating",[6] => "__Friends",[7] => "___Gerald",[8] => "___Gwendolyn",[9] => "_Work",[13] => "__Trips",[14] => "___National",[15] => "___International",[17] => "Other People's Categories",[5] => "_Extreme fishing")formatTreeList($results, $options=array())
New in version 2.7.
Parameters• $results – Results of a find(‘all’) call.
• $options – Options to pass into.
This method will return data similar to find(‘list’) but with a nested prefix that is specified in the spacer option
to show the structure of your data.
Supported options are:• keyPath: A string path to the key, i.e. “{n}.Post.id”.
• valuePath: A string path to the value, i.e. “{n}.Post.title”.

More on models

303

CakePHP Cookbook Documentation, Release 2.x• spacer: The character or characters which will be repeated.
An example would be:$results = $this->Category->find('all');$results = $this->Category->formatTreeList($results, array('spacer' => '--'));
getParentNode()This convenience function will, as the name suggests, return the parent node for any node, or false if the node
has no parent (it’s the root node). For example:$parent = $this->Category->getParentNode(2); //<- id for fun// $parent contains All categories
getPath($id = null, $fields = null, $recursive = null)The ‘path’ when referring to hierarchal data is how you get from where you are to the top. So for example thepath from the category “International” is:• My Categories– ...
– Work– Trips∗ ...
∗ International
Using the id of “International” getPath will return each of the parents in turn (starting from the top).
$parents = $this->Category->getPath(15);// contents of $parents
array([0] => array('Category' => array('id' => 1, 'name' => 'My Categories', ..)),[1] => array('Category' => array('id' => 9, 'name' => 'Work', ..)),[2] => array('Category' => array('id' => 13, 'name' => 'Trips', ..)),[3] => array('Category' => array('id' => 15, 'name' => 'International', ..)
),
)

304

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Advanced Usage
The tree behavior doesn’t only work in the background, there are a number of specific methods defined in the behavior
to cater for all your hierarchical data needs, and any unexpected problems that might arise in the process.
TreeBehavior::moveDown()
Used to move a single node down the tree. You need to provide the ID of the element to be moved and a positive numberof how many positions the node should be moved down. All child nodes for the specified node will also be moved.
Here is an example of a controller action (in a controller named Categories) that moves a specified node down the tree:
public function movedown($id = null, $delta = null) {$this->Category->id = $id;if (!$this->Category->exists()) {throw new NotFoundException(__('Invalid category'));}if ($delta > 0) {$this->Category->moveDown($this->Category->id, abs($delta));} else {$this->Session->setFlash('Please provide the number of positions the field should be' .
'moved down.');}
return $this->redirect(array('action' => 'index'));}For example, if you’d like to move the “Sport” ( id of 3 ) category one position down, you would request: /categories/movedown/3/1.
TreeBehavior::moveUp()
Used to move a single node up the tree. You need to provide the ID of the element to be moved and a positive numberof how many positions the node should be moved up. All child nodes will also be moved.
Here’s an example of a controller action (in a controller named Categories) that moves a node up the tree:
public function moveup($id = null, $delta = null) {$this->Category->id = $id;if (!$this->Category->exists()) {throw new NotFoundException(__('Invalid category'));}if ($delta > 0) {$this->Category->moveUp($this->Category->id, abs($delta));} else {$this->Session->setFlash('Please provide a number of positions the category should' .
'be moved up.');}(continues on next page)

More on models

305

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

return $this->redirect(array('action' => 'index'));}For example, if you would like to move the category “Gwendolyn” ( id of 8 ) up one position you would request/categories/moveup/8/1. Now the order of Friends will be Gwendolyn, Gerald.
TreeBehavior::removeFromTree($id = null, $delete = false)
Using this method will either delete or move a node but retain its sub-tree, which will be reparented one level higher.
It offers more control than delete, which for a model using the tree behavior will remove the specified node and all of
its children.
Taking the following tree as a starting point:• My Categories– Fun∗ Sport· Surfing· Extreme knitting· Skating
Running the following code with the id for ‘Sport’:$this->Node->removeFromTree($id);
The Sport node will be become a top level node:• My Categories– Fun∗ Surfing∗ Extreme knitting∗ Skating• Sport MovedThis demonstrates the default behavior of removeFromTree of moving the node to have no parent, and re-parenting
all children.
If however the following code snippet was used with the id for ‘Sport’:$this->Node->removeFromTree($id, true);
The tree would become• My Categories– Fun∗ Surfing∗ Extreme knitting∗ SkatingThis demonstrates the alternate use for removeFromTree, the children have been reparented and ‘Sport’ has been
deleted.
306

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

TreeBehavior::reorder(array('id' => null, 'field' => $Model->displayField, 'order' => 'ASC', 'verify' => true))
Reorders the nodes (and child nodes) of the tree according to the field and direction specified in the parameters. Thismethod does not change the parent of any node.
$model->reorder(array(//id of record to use as top node for reordering, default: $Model->id'id' => ,//which field to use in reordering, default: $Model->displayField'field' => ,//direction to order, default: 'ASC''order' => ,//whether or not to verify the tree before reorder, default: true
'verify' =>));Note: If you have saved your data or made other operations on the model, you might want to set $model->id = null
before calling reorder. Otherwise only the current node and it’s children will be reordered.

Data Integrity
Due to the nature of complex self referential data structures such as trees and linked lists, they can occasionally become
broken by a careless call. Take heart, for all is not lost! The Tree Behavior contains several previously undocumented
features designed to recover from such situations.
TreeBehavior::recover($mode = 'parent', $missingParentAction = null)
The mode parameter is used to specify the source of info that is valid/correct. The opposite source of data will be
populated based upon that source of info. E.g. if the MPTT fields are corrupt or empty, with the $mode 'parent' thevalues of the parent_id field will be used to populate the left and right fields. The missingParentAction parameter
only applies to “parent” mode and determines what to do if the parent field contains an id that is not present.
Available $mode options:• 'parent' - use the existing parent_id’s to update the lft and rght fields• 'tree' - use the existing lft and rght fields to update parent_id
Available missingParentActions options when using mode='parent':• null - do nothing and carry on• 'return' - do nothing and return• 'delete' - delete the node
• int - set the parent_id to this id
Example:// Rebuild all the left and right fields based on the parent_id$this->Category->recover();// or$this->Category->recover('parent');// Rebuild all the parent_id's based on the lft and rght fields$this->Category->recover('tree');

More on models

307

CakePHP Cookbook Documentation, Release 2.x

TreeBehavior::reorder($options = array())
Reorders the nodes (and child nodes) of the tree according to the field and direction specified in the parameters. Thismethod does not change the parent of any node.
Reordering affects all nodes in the tree by default, however the following options can affect the process:• 'id' - only reorder nodes below this node.
• 'field’ - field to use for sorting, default is the displayField for the model.
• 'order' - 'ASC' for ascending, 'DESC' for descending sort.
• 'verify' - whether or not to verify the tree prior to resorting.
$options is used to pass all extra parameters, and has the following possible keys by default, all of which are optional:
array('id' => null,'field' => $model->displayField,'order' => 'ASC','verify' => true)
TreeBehavior::verify()
Returns true if the tree is valid otherwise an array of errors, with fields for type, incorrect index and message.
Each record in the output array is an array of the form (type, id, message)• type is either 'index' or 'node'• 'id' is the id of the erroneous node.
• 'message' depends on the error
Example Use:
$this->Category->verify();
Example output:
Array([0] => Array([0] => node[1] => 3[2] => left and right values identical)[1] => Array([0] => node[1] => 2[2] => The parent node 999 doesn't exist)[10] => Array([0] => index[1] => 123(continues on next page)

308

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[2] => missing)[99] => Array([0] => node[1] => 163[2] => left greater than right))

Node Level (Depth)
New in version 2.7.
Knowing the depth of tree nodes can be useful when you want to retrieve nodes only upto a certain level for e.g. whengenerating menus. You can use the level option to specify the field that will save level of each node:
public $actsAs = array('Tree' => array('level' => 'level', // Defaults to null, i.e. no level saving));
TreeBehavior::getLevel($id)
New in version 2.7.
If you are not caching the level of nodes using the level option in settings, you can use this method to get level of aparticular node.
Using Behaviors
Behaviors are attached to models through the $actsAs model class variable:class Category extends AppModel {public $actsAs = array('Tree');}This example shows how a Category model could be managed in a tree structure using the TreeBehavior. Once a
behavior has been specified, use the methods added by the behavior as if they always existed as part of the originalmodel:// Set ID$this->Category->id = 42;// Use behavior method, children():$kids = $this->Category->children();Some behaviors may require or allow settings to be defined when the behavior is attached to the model. Here, we tell
our TreeBehavior the names of the “left” and “right” fields in the underlying database table:class Category extends AppModel {public $actsAs = array('Tree' => array('left' => 'left_node',(continues on next page)

More on models

309

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'right' => 'right_node'));}We can also attach several behaviors to a model. There’s no reason why, for example, our Category model should only
behave as a tree, it may also need internationalization support:
class Category extends AppModel {public $actsAs = array('Tree' => array('left' => 'left_node','right' => 'right_node'),'Translate');}
So far we have been adding behaviors to models using a model class variable. That means that our behaviors will be
attached to our models throughout the model’s lifetime. However, we may need to “detach” behaviors from our models
at runtime. Let’s say that on our previous Category model, which is acting as a Tree and a Translate model, we needfor some reason to force it to stop acting as a Translate model:// Detach a behavior from our model:$this->Category->Behaviors->unload('Translate');That will make our Category model stop behaving as a Translate model from thereon. We may need, instead, to justdisable the Translate behavior from acting upon our normal model operations: our finds, our saves, etc. In fact, we arelooking to disable the behavior from acting upon our CakePHP model callbacks. Instead of detaching the behavior, wethen tell our model to stop informing of these callbacks to the Translate behavior:// Stop letting the behavior handle our model callbacks$this->Category->Behaviors->disable('Translate');We may also need to find out if our behavior is handling those model callbacks, and if not we then restore its ability toreact to them:// If our behavior is not handling model callbacks
if (!$this->Category->Behaviors->enabled('Translate')) {// Tell it to start doing so$this->Category->Behaviors->enable('Translate');}Just as we could completely detach a behavior from a model at runtime, we can also attach new behaviors. Say that ourfamiliar Category model needs to start behaving as a Christmas model, but only on Christmas day:// If today is Dec 25
if (date('m/d') === '12/25') {// Our model needs to behave as a Christmas model$this->Category->Behaviors->load('Christmas');}We can also use the load method to override behavior settings:

310

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x// We will change one setting from our already attached behavior$this->Category->Behaviors->load('Tree', array('left' => 'new_left_node'));And using aliasing, we can customize the alias it will be loaded as, also allowing it to be loaded multiple times withdifferent settings:// The behavior will be available as 'MyTree'$this->Category->Behaviors->load('MyTree', array('className' => 'Tree'));There’s also a method to obtain the list of behaviors a model has attached. If we pass the name of a behavior to themethod, it will tell us if that behavior is attached to the model, otherwise it will give us the list of attached behaviors:// If the Translate behavior is not attachedif (!$this->Category->Behaviors->loaded('Translate')) {// Get the list of all behaviors the model has attached$behaviors = $this->Category->Behaviors->loaded();}Creating Behaviors
Behaviors that are attached to Models get their callbacks called automatically. The callbacks are similar to those found in
Models: beforeFind, afterFind, beforeValidate, afterValidate, beforeSave, afterSave, beforeDelete,afterDelete and onError - see Callback Methods.
Your behaviors should be placed in app/Model/Behavior. They are named in CamelCase and postfixed by Behavior,
ex. NameBehavior.php. It’s often helpful to use a core behavior as a template when creating your own. Find them inlib/Cake/Model/Behavior/.
Every callback and behavior method takes a reference to the model it is being called from as the first parameter.
Besides implementing the callbacks, you can add settings per behavior and/or model behavior attachment. Information
about specifying settings can be found in the chapters about core behaviors and their configuration.
A quick example that illustrates how behavior settings can be passed from the model to the behavior:class Post extends AppModel {public $actsAs = array('YourBehavior' => array('option1_key' => 'option1_value'));}
Since behaviors are shared across all the model instances that use them, it’s a good practice to store the settings peralias/model name that is using the behavior. When created behaviors will have their setup() method called:
public function setup(Model $Model, $settings = array()) {if (!isset($this->settings[$Model->alias])) {$this->settings[$Model->alias] = array('option1_key' => 'option1_default_value','option2_key' => 'option2_default_value','option3_key' => 'option3_default_value',);}(continues on next page)

More on models

311

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], (array)$settings);}

Creating behavior methods
Behavior methods are automatically available on any model acting as the behavior. For example if you had:class Duck extends AppModel {public $actsAs = array('Flying');}
You would be able to call FlyingBehavior methods as if they were methods on your Duck model. When creating
behavior methods you automatically get passed a reference of the calling model as the first parameter. All other supplied
parameters are shifted one place to the right. For example:$this->Duck->fly('toronto', 'montreal');Although this method takes two parameters, the method signature should look like:public function fly(Model $Model, $from, $to) {// Do some flying.
}
Keep in mind that methods called in a $this->doIt() fashion from inside a behavior method will not get the $model
parameter automatically appended.
Mapped methods
In addition to providing ‘mixin’ methods, behaviors can also provide pattern matching methods. Behaviors can also
define mapped methods. Mapped methods use pattern matching for method invocation. This allows you to create
methods similar to Model::findAllByXXX methods on your behaviors. Mapped methods need to be declared in yourbehaviors $mapMethods array. The method signature for a mapped method is slightly different than a normal behavior
mixin method:class MyBehavior extends ModelBehavior {public $mapMethods = array('/do(\w+)/' => 'doSomething');
public function doSomething(Model $model, $method, $arg1, $arg2) {debug(func_get_args());//do something}}
The above will map every doXXX() method call to the behavior. As you can see, the model is still the first parameter,
but the called method name will be the 2nd parameter. This allows you to munge the method name for additional
information, much like Model::findAllByXX. If the above behavior was attached to a model the following would
happen:$model->doReleaseTheHounds('karl', 'lenny');(continues on next page)

312

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// would output'ReleaseTheHounds', 'karl', 'lenny'

Behavior callbacks
Model Behaviors can define a number of callbacks that are triggered before the model callbacks of the same name.
Behavior callbacks allow your behaviors to capture events in attached models and augment the parameters or splice inadditional behavior.
All behavior callbacks are fired before the model callbacks are:• beforeFind• afterFind• beforeValidate• afterValidate• beforeSave• afterSave• beforeDelete• afterDelete
Creating a behavior callbackclass ModelBehavior
Model behavior callbacks are defined as simple methods in your behavior class. Much like regular behavior methods,
they receive a $Model parameter as the first argument. This parameter is the model that the behavior method was
invoked on.
ModelBehavior::setup(Model $Model, array $settings = array())
Called when a behavior is attached to a model. The settings come from the attached model’s $actsAs property.
ModelBehavior::cleanup(Model $Model)
Called when a behavior is detached from a model. The base method removes model settings based on$model->alias. You can override this method and provide custom cleanup functionality.
ModelBehavior::beforeFind(Model $Model, array $query)
If a behavior’s beforeFind return’s false it will abort the find(). Returning an array will augment the queryparameters used for the find operation.
ModelBehavior::afterFind(Model $Model, mixed $results, boolean $primary = false)
You can use the afterFind to augment the results of a find. The return value will be passed on as the results toeither the next behavior in the chain or the model’s afterFind.
ModelBehavior::beforeValidate(Model $Model, array $options = array())
You can use beforeValidate to modify a model’s validate array or handle any other pre-validation logic. Returning
false from a beforeValidate callback will abort the validation and cause it to fail.
ModelBehavior::afterValidate(Model $Model)
You can use afterValidate to perform any data cleanup or preparation if needed.

More on models

313

CakePHP Cookbook Documentation, Release 2.x

ModelBehavior::beforeSave(Model $Model, array $options = array())
You can return false from a behavior’s beforeSave to abort the save. Return true to allow it continue.
ModelBehavior::afterSave(Model $Model, boolean $created, array $options = array())
You can use afterSave to perform clean up operations related to your behavior. $created will be true when arecord is created, and false when a record is updated.
ModelBehavior::beforeDelete(Model $Model, boolean $cascade = true)
You can return false from a behavior’s beforeDelete to abort the delete. Return true to allow it continue.
ModelBehavior::afterDelete(Model $Model)
You can use afterDelete to perform clean up operations related to your behavior.

DataSources
DataSources are the link between models and the source of data that models represent. In many cases, the data is retrieved from a relational database such as MySQL, PostgreSQL or Microsoft SQL Server. CakePHP is distributed withseveral database-specific datasources (see the class files in lib/Cake/Model/Datasource/Database), a summaryof which is listed here for your convenience:• Mysql• Postgres• Sqlite• Sqlserver
Note: You can find additional community contributed datasources in the CakePHP DataSources repository on
GitHub63 .
When specifying a database connection configuration in app/Config/database.php, CakePHP transparently uses
the corresponding database datasource for all model operations. So, even though you might not have known about
datasources, you’ve been using them all along.
All of the above sources derive from a base DboSource class, which aggregates some logic that is common to mostrelational databases. If you decide to write a RDBMS datasource, working from one of these (e.g. MySQL, or SQLite)is your best bet.
Most people, however, are interested in writing datasources for external sources of data, such as remote REST APIs oreven an LDAP server. So that’s what we’re going to look at now.
Basic API For DataSources
A datasource can, and should implement at least one of the following methods: create, read, update and/or delete(the actual method signatures & implementation details are not important for the moment, and will be described later).
You need not implement more of the methods listed above than necessary - if you need a read-only datasource, there’s
no reason to implement create, update, and delete.
Methods that must be implemented for all CRUD methods:• describe($model)• listSources($data = null)
63 https://github.com/cakephp/datasources/tree/2.0314

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x• calculate($model, $func, $params)• At least one of:– create(Model $model, $fields = null, $values = null)– read(Model $model, $queryData = array(), $recursive = null)– update(Model $model, $fields = null, $values = null, $conditions = null)– delete(Model $model, $id = null)
It is also possible (and sometimes quite useful) to define the $_schema class attribute inside the datasource itself,
instead of in the model.
And that’s pretty much all there is to it. By coupling this datasource to a model, you are then able to use
Model::find()/save()/delete() as you would normally, and the appropriate data and/or parameters used to callthose methods will be passed on to the datasource itself, where you can decide to implement whichever features youneed (e.g. Model::find options such as 'conditions' parsing, 'limit' or even your own custom parameters).
An Example
A common reason you would want to write your own datasource is when you would like to access a 3rd party API using
the usual Model::find()/save()/delete() methods. Let’s write a datasource that will access a fictitious remote
JSON based API. We’ll call it FarAwaySource and we’ll put it in app/Model/Datasource/FarAwaySource.php:
App::uses('HttpSocket', 'Network/Http');class FarAwaySource extends DataSource {/*** An optional description of your datasource*/
public $description = 'A far away datasource';/*** Our default config options. These options will be customized in our* ``app/Config/database.php`` and will be merged in the ``__construct()``.
*/
public $config = array('apiKey' => '',);/*** If we want to create() or update() we need to specify the fields* available. We use the same array keys as we do with CakeSchema, eg.
* fixtures and schema migrations.
*/protected $_schema = array('id' => array('type' => 'integer','null' => false,'key' => 'primary','length' => 11,),'name' => array((continues on next page)

More on models

315CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'type' => 'string','null' => true,'length' => 255,),
'message' => array('type' => 'text','null' => true,
),
);
/**
* Create our HttpSocket and handle any config tweaks.
*/
public function __construct($config) {parent::__construct($config);$this->Http = new HttpSocket();}/*** Since datasources normally connect to a database there are a few things* we must change to get them to work without a database.
*//*** listSources() is for caching. You'll likely want to implement caching in* your own way with a custom datasource. So just ``return null``.
*/public function listSources($data = null) {return null;}/*** describe() tells the model your schema for ``Model::save()``.
** You may want a different schema for each model but still use a single* datasource. If this is your case then set a ``schema`` property on your* models and simply return ``$model->schema`` here instead.
*/public function describe($model) {return $this->_schema;}/*** calculate() is for determining how we will count the records and is* required to get ``update()`` and ``delete()`` to work.
** We don't count the records here but return a string to be passed to* ``read()`` which will do the actual counting. The easiest way is to just* return the string 'COUNT' and check for it in ``read()`` where* ``$data['fields'] === 'COUNT'``.
*/
public function calculate(Model $model, $func, $params = array()) {(continues on next page)

316

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

return 'COUNT';}/*** Implement the R in CRUD. Calls to ``Model::find()`` arrive here.
*/
public function read(Model $model, $queryData = array(),$recursive = null) {/*** Here we do the actual count as instructed by our calculate()* method above. We could either check the remote source or some* other way to get the record count. Here we'll simply return 1 so
* ``update()`` and ``delete()`` will assume the record exists.
*/
if ($queryData['fields'] === 'COUNT') {return array(array(array('count' => 1)));}/*** Now we get, decode and return the remote data.
*/$queryData['conditions']['apiKey'] = $this->config['apiKey'];$json = $this->Http->get('http://example.com/api/list.json',$queryData['conditions']);$res = json_decode($json, true);if (is_null($res)) {$error = json_last_error();throw new CakeException($error);}
return array($model->alias => $res);}/*** Implement the C in CRUD. Calls to ``Model::save()`` without $model->id* set arrive here.
*/
public function create(Model $model, $fields = null, $values = null) {$data = array_combine($fields, $values);$data['apiKey'] = $this->config['apiKey'];$json = $this->Http->post('http://example.com/api/set.json', $data);$res = json_decode($json, true);if (is_null($res)) {$error = json_last_error();throw new CakeException($error);}
return true;}/*** Implement the U in CRUD. Calls to ``Model::save()`` with $Model->id* set arrive here. Depending on the remote source you can just call(continues on next page)

More on models

317CakePHP Cookbook Documentation, Release 2.x(continued from previous page)* ``$this->create()``.
*/
public function update(Model $model, $fields = null, $values = null,$conditions = null) {return $this->create($model, $fields, $values);}/*** Implement the D in CRUD. Calls to ``Model::delete()`` arrive here.
*/
public function delete(Model $model, $id = null) {$json = $this->Http->get('http://example.com/api/remove.json', array('id' => $id[$model->alias . '.id'],'apiKey' => $this->config['apiKey'],));$res = json_decode($json, true);if (is_null($res)) {$error = json_last_error();throw new CakeException($error);}
return true;}}
We can then configure the datasource in our app/Config/database.php file by adding something like this:public $faraway = array('datasource' => 'FarAwaySource','apiKey'=> '1234abcd',);
Then use the database config in our models like this:class MyModel extends AppModel {public $useDbConfig = 'faraway';}
We can retrieve data from our remote source using the familiar model methods:// Get all messages from 'Some Person'$messages = $this->MyModel->find('all', array('conditions' => array('name' => 'Some Person'),));

Tip: Using find types other than 'all' can have unexpected results if the result of your read method is not a
numerically indexed array.
Similarly we can save a new message:

318

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x$this->MyModel->save(array('name' => 'Some Person','message' => 'New Message',));
Update the previous message:$this->MyModel->id = 42;$this->MyModel->save(array('message' => 'Updated message',));And delete the message:$this->MyModel->delete(42);

Plugin DataSources
You can also package Datasources into plugins.
Simply place your datasource file into Plugin/[YourPlugin]/Model/Datasource/[YourSource].php and refer
to it using the plugin notation:
public $faraway = array('datasource' => 'MyPlugin.FarAwaySource','apiKey'=> 'abcd1234',);

Connecting to SQL Server
The Sqlserver datasource depends on Microsoft’s PHP extension called pdo_sqlsrv64 . This PHP Extension is notincluded in the base installation of PHP and must be installed separately. The SQL Server Native Client must also be
installed for the extension to work.
So if the Sqlserver Datasource errors out with:
Error: Database connection "Sqlserver" is missing, or could not be created.
First check if the SQL Server PHP extension pdo_sqlsrv and the SQL Server Native Client are installed properly.

Model Attributes
Model attributes allow you to set properties that can override the default model behavior.
For a complete list of model attributes and their descriptions visit the CakePHP API65 .
64 https://github.com/Microsoft/msphpsql

65 https://api.cakephp.org/2.x/class-Model.html

More on models

319CakePHP Cookbook Documentation, Release 2.x

useDbConfig
The useDbConfig property is a string that specifies the name of the database connection to use to bind your model
class to the related database table. You can set it to any of the database connections defined within your databaseconfiguration file. The database configuration file is stored in /app/Config/database.php.
The useDbConfig property is defaulted to the ‘default’ database connection.
Example usage:class Example extends AppModel {public $useDbConfig = 'alternate';}

useTable
The useTable property specifies the database table name. By default, the model uses the lowercase, plural form of
the model’s class name. Set this attribute to the name of an alternate table, or set it to false if you wish the model touse no database table.
Example usage:class Example extends AppModel {public $useTable = false; // This model does not use a database table}
Alternatively:
class Example extends AppModel {public $useTable = 'exmp'; // This model uses a database table 'exmp'}

tablePrefix
The name of the table prefix used for the model. The table prefix is initially set in the database connection file at/app/Config/database.php. The default is no prefix. You can override the default by setting the tablePrefix attributein the model.
Example usage:class Example extends AppModel {public $tablePrefix = 'alternate_'; // will look for 'alternate_examples'}

320

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

primaryKey
Each table normally has a primary key, id. You may change which field name the model uses as its primary key. Thisis common when setting CakePHP to use an existing database table.
Example usage:class Example extends AppModel {// example_id is the field name in the databasepublic $primaryKey = 'example_id';}displayField
The displayField attribute specifies which database field should be used as a label for the record. The label is usedin scaffolding and in find('list') calls. The model will use name or title, by default.
For example, to use the username field:
class User extends AppModel {public $displayField = 'username';}
Multiple field names cannot be combined into a single display field. For example, you cannot specify,
array('first_name', 'last_name') as the display field. Instead create a virtual field with the Model attribute
virtualFields
recursive
The recursive property defines how deep CakePHP should go to fetch associated model data via find(), and read()methods.
Imagine your application features Groups which belong to a Domain and have many Users which in turn have many
Articles. You can set $recursive to different values based on the amount of data you want back from a $this->Group>find() call:• -1 CakePHP fetches Group data only, no joins.
• 0 CakePHP fetches Group data and its Domain• 1 CakePHP fetches a Group, its Domain and its associated Users• 2 CakePHP fetches a Group, its Domain, its associated Users, and the Users’ associated Articles
Set it no higher than you need. Having CakePHP fetch data you aren’t going to use slows your app unnecessarily. Also
note that the default recursive level is 1.
Note: If you want to combine $recursive with the fields functionality, you will have to add the columns containing
the required foreign keys to the fields array manually. In the example above, this could mean adding domain_id.
The recommended recursive level for your application should be -1. This avoids retrieving related data where that is
unnecessary or even unwanted. This is most likely the case for most of your find() calls. Raise it only when needed or
use Containable behavior.
You can achieve that by adding it to the AppModel:

More on models

321CakePHP Cookbook Documentation, Release 2.x

public $recursive = -1;
If you use events in your system, using the value -1 for recursive will disable all event triggering in the associatedmodel. This happens because no relations are created when the value is set to -1.
order
The default ordering of data for any find operation. Possible values include:$order = "field"$order = "Model.field";$order = "Model.field asc";$order = "Model.field ASC";$order = "Model.field DESC";$order = array("Model.field" => "asc", "Model.field2" => "DESC");data
The container for the model’s fetched data. While data returned from a model class is normally used as returned froma find() call, you may need to access information stored in $data inside of model callbacks.
_schema
Contains metadata describing the model’s database table fields. Each field is described by:• name• type
The types CakePHP supports are:stringGenerally backed by CHAR or VARCHAR columns. In SQL Server, NCHAR and NVARCHAR types are used.
text
Maps to TEXT, MONEY types.
uuid
Maps to the UUID type if a database provides one, otherwise this will generate a CHAR(36) field.
tinyinteger
Maps to the TINYINT or SMALLINT type provided by the database.
smallinteger
Maps to the SMALLINT type provided by the database.
integer
Maps to the INTEGER types provided by the database.
biginteger
Maps to the BIGINT type provided by the database.
decimal
Maps to the DECIMAL or NUMERIC types.
float
Maps to the REAL, DOUBLE PRECISION types.
322

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

booleanMaps to BOOLEAN except in MySQL, where TINYINT(1) is used to represent booleans.
binaryMaps to the BLOB or BYTEA type provided by the database.
date
Maps to a timezone naive DATE column type.
datetime
Maps to a timezone naive DATETIME column type. In PostgreSQL, and SQL Server this turns into a TIMESTAMP or TIMESTAMPTZ type.
timestamp
Maps to the TIMESTAMP type.
time
Maps to a TIME type in all databases.
• null• default value• length
Example Usage:
protected $_schema = array('first_name' => array('type' => 'string','length' => 30),'last_name' => array('type' => 'string','length' => 30),'email' => array('type' => 'string','length' => 30),'message' => array('type' => 'text'));
Changed in version 2.10.0: The smallinteger and tinyinteger types were added in 2.10.0
validate
This attribute holds rules that allow the model to make data validation decisions before saving. Keys named after fields
hold regex values allowing the model to try to make matches.
Note: It is not necessary to call validate() before save() as save() will automatically validate your data before actuallysaving.
For more information on validation, see the Data Validation later on in this manual.

More on models

323

CakePHP Cookbook Documentation, Release 2.x

virtualFields
Array of virtual fields this model has. Virtual fields are aliased SQL expressions. Fields added to this property will beread as other fields in a model but will not be saveable.
Example usage for MySQL:
public $virtualFields = array('name' => "CONCAT(User.first_name, ' ', User.last_name)");
In subsequent find operations, your User results would contain a name key with the result of the concatenation. It is
not advisable to create virtual fields with the same names as columns on the database, this can cause SQL errors.
For more information on the virtualFields property, its proper usage, as well as limitations, see Virtual fields.
name
Name of the model. If you do not specify it in your model file it will be set to the class name by constructor.
Example usage:class Example extends AppModel {public $name = 'Example';}cacheQueries
If set to true, data fetched by the model during a single request is cached. This caching is in-memory only, and only
lasts for the duration of the request. Any duplicate requests for the same data is handled by the cache.

Additional Methods and Properties
While CakePHP’s model functions should get you where you need to go, don’t forget that model classes are just that:classes that allow you to write your own methods or define your own properties.
Any operation that handles the saving and fetching of data is best housed in your model classes. This concept is often
referred to as the fat model.
class Example extends AppModel {public function getRecent() {$conditions = array('created BETWEEN (curdate() - interval 7 day)' .
' and (curdate() - interval 0 day))');
return $this->find('all', compact('conditions'));}}This getRecent() method can now be used within the controller.
$recent = $this->Example->getRecent();

324

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Model::associations()
Get associations:$result = $this->Example->associations();// $result equals array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany')

Model::buildQuery(string $type = 'first', array $query = array())
Builds the query array that is used by the data source to generate the query to fetch the data.
Model::deconstruct(string $field, mixed $data)
Deconstructs a complex data type (array or object) into a single field value.
Model::escapeField(string $field = null, string $alias = null)
Escapes the field name and prepends the model name. Escaping is done according to the current database driver’s
rules.
Model::exists($id)
Returns true if a record with the particular ID exists.
If ID is not provided it calls Model::getID() to obtain the current record ID to verify, and then performs a
Model::find('count') on the currently configured datasource to ascertain the existence of the record in persistent storage.
Note: Parameter $id was added in 2.1. Prior to that it does not take any parameter.
$this->Example->id = 9;
if ($this->Example->exists()) {// ...
}$exists = $this->Foo->exists(2);Model::getAffectedRows()
Returns the number of rows affected by the last query.

More on models

325

CakePHP Cookbook Documentation, Release 2.x

Model::getAssociated(string $type = null)Gets all the models with which this model is associated.
Model::getColumnType(string $column)
Returns the column type of a column in the model.
Model::getColumnTypes()
Returns an associative array of field names and column types.
Model::getID(integer $list = 0)
Returns the current record’s ID.
Model::getInsertID()
Returns the ID of the last record this model inserted.
Model::getLastInsertID()
Alias to getInsertID().

Virtual fields
Virtual fields allow you to create arbitrary SQL expressions and assign them as fields in a Model. These fields cannot
be saved, but will be treated like other model fields for read operations. They will be indexed under the model’s key
alongside other model fields.
Creating virtual fields
Creating virtual fields is easy. In each model you can define a $virtualFields property that contains an array offield => expressions. An example of a virtual field definition using MySQL would be:
public $virtualFields = array('name' => 'CONCAT(User.first_name, " ", User.last_name)');
And with PostgreSQL:
public $virtualFields = array('name' => "User.first_name || \' \' || User.last_name");
In subsequent find operations, your User results would contain a name key with the result of the concatenation. It is
not advisable to create virtual fields with the same names as columns on the database, this can cause SQL errors.

326

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

It is not always useful to have User.first_name fully qualified. If you do not follow the convention (i.e. you havemultiple relations to other tables) this would result in an error. In this case it may be better to just use first_name ||\' \' || last_name without the Model Name.
Using virtual fields
Creating virtual fields is straightforward and easy, interacting with virtual fields can be done through a few different
methods.
Model::hasField()
Model::hasField() will return true if the model has a concrete field passed by the first parameter. By setting the second
parameter of hasField() to true, virtualFields will also be checked when checking if a model has a field. Using theexample field above:// Will return false, as there is no concrete field called name$this->User->hasField('name');// Will return true as there is a virtual field called name$this->User->hasField('name', true);

Model::isVirtualField()This method can be used to check if a field/column is a virtual field or a concrete field. Will return true if the column
is virtual:$this->User->isVirtualField('name'); //true$this->User->isVirtualField('first_name'); //false

Model::getVirtualField()This method can be used to access the SQL expression that comprises a virtual field. If no argument is supplied it will
return all virtual fields in a Model://returns 'CONCAT(User.first_name, ' ', User.last_name)'$this->User->getVirtualField('name');Model::find() and virtual fields
As stated earlier Model::find() will treat virtual fields much like any other field in a model. The value of a virtual
field will be placed under the model’s key in the resultset:$results = $this->User->find('first');// results contains the following
array('User' => array('first_name' => 'Mark','last_name' => 'Story',(continues on next page)

More on models

327CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'name' => 'Mark Story',//more fields.
));

Pagination and virtual fields
Since virtual fields behave much like regular fields when doing finds, Controller::paginate() will be able to sortby virtual fields too.
Virtual fields and model aliases
When you are using virtualFields and models with aliases that are not the same as their name, you can run into problems
as virtualFields do not update to reflect the bound alias. If you are using virtualFields in models that have more than
one alias it is best to define the virtualFields in your model’s constructor:
public function __construct($id = false, $table = null, $ds = null) {parent::__construct($id, $table, $ds);$this->virtualFields['name'] = sprintf('CONCAT(%s.first_name, " ", %s.last_name)', $this->alias, $this->alias);}
This will allow your virtualFields to work for any alias you give a model.
Pagination and Virtual fields set in controller with JOINSThe following example allows you to have a counter of a hasMany association and enables you to use virtual fields. For
example if you had the following sort link in your template:// Create a sort link for a virtual field$this->Paginator->sort('ProductsItems.Total','Items Total');
You could then use the following pagination setup in your controller:$this->Products->recursive = -1;// Products hasMany associations ProductsItems$this->Products->ProductsItems->virtualFields['Total'] = 'count(ProductsItems.products_
˓→id)';// Where ORM$where = array('fields' => array('Products.*','count(ProductsItems.products_id) AS ProductsItems__Total',),'joins' => array(
array('table' => 'products_items',(continues on next page)

328

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'alias' => 'ProductsItems','type' => 'LEFT','conditions' => array('ProductsItems.products_id = Products.id',))),'group' => 'ProductsItems.products_id');// Set conditions Paginator$this->paginate = $where;// Get data
$data = $this->Paginator->paginate();would return something like this:Array([0] => Array([Products] => Array([id] => 1234,[description] => 'Text bla bla...',)[ProductsItems] => Array([Total] => 25))[1] => Array([Products] => Array([id] => 4321,[description] => 'Text 2 bla bla...',)[ProductsItems] => Array([Total] => 50)))

More on models

329CakePHP Cookbook Documentation, Release 2.x

Virtual fields in SQL queries
Using functions in direct SQL queries will prevent data from being returned in the same array as your model’s data.
For example this:$this->Timelog->query("SELECT
project_id, SUM(id) as TotalHours
FROM
timelogs
AS
Timelog
GROUP BY
project_id;");
would return something like this:Array([0] => Array([Timelog] => Array([project_id] => 1234)[0] => Array([TotalHours] => 25.5)))
If we want to group TotalHours into our Timelog array we should specify a virtual field for our aggregate column. We
can add this new virtual field on the fly rather than permanently declaring it in the model. We will provide a default
value of 0 in case another query attempts to use this virtual field. If that were to occur, 0 would be returned in the
TotalHours column:$this->Timelog->virtualFields['TotalHours'] = 0;
In addition to adding the virtual field we also need to alias our column using the form of MyModel__MyField like this:$this->Timelog->query("SELECT
project_id, SUM(id) as Timelog__TotalHours
FROM
timelogs
AS
Timelog
GROUP BY
project_id;");
Running the query again after specifying the virtual field should result in a cleaner grouping of values:

330

Chapter 6. Models

CakePHP Cookbook Documentation, Release 2.x

Array([0] => Array([Timelog] => Array([project_id] => 1234
[TotalHours] => 25.5)))

Limitations of virtualFields
The implementation of virtualFields has a few limitations. First you cannot use virtualFields on associatedmodels for conditions, order, or fields arrays. Doing so will generally result in an SQL error as the fields are not replacedby the ORM. This is because it difficult to estimate the depth at which an associated model might be found.
A common workaround for this implementation issue is to copy virtualFields from one model to another at runtime
when you need to access them:$this->virtualFields['name'] = $this->Author->virtualFields['name'];or:$this->virtualFields += $this->Author->virtualFields;

Transactions
To perform a transaction, a model’s table must be of a datasource and type which supports transactions.
All transaction methods must be performed on a model’s DataSource object. To get a model’s DataSource from within
the model, use:
$dataSource = $this->getDataSource();
You can then use the data source to start, commit, or roll back transactions.
$dataSource->begin();// Perform some tasks
if (/*all's well*/) {$dataSource->commit();} else {$dataSource->rollback();}More on models

331CakePHP Cookbook Documentation, Release 2.x

Nested Transactions
It is possible to start a transaction several times using the Datasource::begin() method. The transaction will finish
only when the number of commit and rollback calls match with begin.
$dataSource->begin();// Perform some tasks$dataSource->begin();// More few tasks
if (/*latest task ok*/) {$dataSource->commit();} else {$dataSource->rollback();// Change something in main task}
$dataSource->commit();This will perform the real nested transaction if your database supports it and it is enabled in the datasource. The methods
will always return true when in transaction mode and the nested is not supported or disabled.
If you want to use multiple begin’s but not use the nested transaction from database, disable it using$dataSource->useNestedTransactions = false;. It will use only one global transaction.
The real nested transaction is disabled by default. Enable it using $dataSource->useNestedTransactions =true;.

332

Chapter 6. Models

CHAPTER 7

Core Libraries

CakePHP comes with a plethora of built-in functions and classes. These classes and functions try to cover some of themost common features required in web applications.

General Purpose
General purpose libraries are available and reused in many places across CakePHP.

General Purpose
Global Constants and Functions
While most of your day-to-day work in CakePHP will be utilizing core classes and methods, CakePHP features anumber of global convenience functions that may come in handy. Many of these functions are for use with CakePHP
classes (loading model or component classes), but many others make working with arrays or strings a little easier.
We’ll also cover some of the constants available in CakePHP applications. Using these constants will help makeupgrades more smooth, but are also convenient ways to point to certain files or directories in your CakePHP application.

333

CakePHP Cookbook Documentation, Release 2.x

Global Functions
Here are CakePHP’s globally available functions. Most of them are just convenience wrappers for other CakePHP
functionality, such as debugging and translating content.
\_\_(string $string_id, [$formatArgs])
This function handles localization in CakePHP applications. The $string_id identifies the ID for a translation.
Strings used for translations are treated as format strings for sprintf(). You can supply additional arguments
to replace placeholders in your string:__('You have %s unread messages', h($number));

Note: Check out the Internationalization & Localization section for more information.
__c(string $msg, integer $category, mixed $args = null)
Note that the category must be specified with an I18n class constant, instead of only the constant name. The
values are:• I18n::LC_ALL - LC_ALL• I18n::LC_COLLATE - LC_COLLATE• I18n::LC_CTYPE - LC_CTYPE• I18n::LC_MONETARY - LC_MONETARY• I18n::LC_NUMERIC - LC_NUMERIC• I18n::LC_TIME - LC_TIME• I18n::LC_MESSAGES - LC_MESSAGES__d(string $domain, string $msg, mixed $args = null)
Allows you to override the current domain for a single message lookup.
Useful when internationalizing a plugin: echo __d('plugin_name', 'This is my plugin');__dc(string $domain, string $msg, integer $category, mixed $args = null)
Allows you to override the current domain for a single message lookup. It also allows you to specify a category.
Note that the category must be specified with an I18n class constant, instead of only the constant name. The
values are:• I18n::LC_ALL - LC_ALL• I18n::LC_COLLATE - LC_COLLATE• I18n::LC_CTYPE - LC_CTYPE• I18n::LC_MONETARY - LC_MONETARY• I18n::LC_NUMERIC - LC_NUMERIC• I18n::LC_TIME - LC_TIME• I18n::LC_MESSAGES - LC_MESSAGES__dcn(string $domain, string $singular, string $plural, integer $count, integer $category, mixed $args = null)
Allows you to override the current domain for a single plural message lookup. It also allows you to specify
a category. Returns correct plural form of message identified by $singular and $plural for count $count fromdomain $domain.
334

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Note that the category must be specified with an I18n class constant, instead of only the constant name. The
values are:• I18n::LC_ALL - LC_ALL• I18n::LC_COLLATE - LC_COLLATE• I18n::LC_CTYPE - LC_CTYPE• I18n::LC_MONETARY - LC_MONETARY• I18n::LC_NUMERIC - LC_NUMERIC• I18n::LC_TIME - LC_TIME• I18n::LC_MESSAGES - LC_MESSAGES__dn(string $domain, string $singular, string $plural, integer $count, mixed $args = null)
Allows you to override the current domain for a single plural message lookup. Returns correct plural form of
message identified by $singular and $plural for count $count from domain $domain.
__x(string $context, string $singular, mixed $args = null)
The context is a unique identifier for the translations string that makes it unique within the same domain.
__xn(string $context, string $singular, string $plural, integer $count, mixed $args = null)
Returns correct plural form of the message identified by $singular and $plural for count $count. It also
allows you to specify a context. Some languages have more than one form for plural messages dependent on the
count.
The context is a unique identifier for the translations string that makes it unique within the same domain.
__dx(string $domain, string $context, string $msg, mixed $args = null)
Allows you to override the current domain for a single message lookup. It also allows you to specify a context.
The context is a unique identifier for the translations string that makes it unique within the same domain.
__dxn(string $domain, string $context, string $singular, string $plural, integer $count, mixed $args = null)
Allows you to override the current domain and context for a single plural message lookup. Returns correct pluralform of message identified by $singular and $plural for count $count from domain $domain. Some languages
have more than one form for plural messages dependent on the count.
The context is a unique identifier for the translation string that makes it unique within the same domain.
__dxc(string $domain, string $context, string $msg, integer $category, mixed $args = null)
Allows you to override the current domain for a single message lookup. It also allows you to specify a category
and a context.
The context is a unique identifier for the translations string that makes it unique within the same domain.
Note that the category must be specified with an I18n class constant, instead of only the constant name. The
values are:• I18n::LC_ALL - LC_ALL• I18n::LC_COLLATE - LC_COLLATE• I18n::LC_CTYPE - LC_CTYPE• I18n::LC_MONETARY - LC_MONETARY• I18n::LC_NUMERIC - LC_NUMERIC• I18n::LC_TIME - LC_TIME• I18n::LC_MESSAGES - LC_MESSAGESGeneral Purpose

335

CakePHP Cookbook Documentation, Release 2.x__xc(string $context, string $msg, integer $count, integer $category, mixed $args = null)
The context is a unique identifier for the translations string that makes it unique within the same domain.
Note that the category must be specified with an I18n class constant, instead of only the constant name. The
values are:• I18n::LC_ALL - LC_ALL• I18n::LC_COLLATE - LC_COLLATE• I18n::LC_CTYPE - LC_CTYPE• I18n::LC_MONETARY - LC_MONETARY• I18n::LC_NUMERIC - LC_NUMERIC• I18n::LC_TIME - LC_TIME• I18n::LC_MESSAGES - LC_MESSAGES__dxcn(string $domain, string $context, string $singular, string $plural, integer $count, integer $category, mixed$args = null)
Allows you to override the current domain for a single plural message lookup. It also allows you to specify acategory and a context. Returns correct plural form of message identified by $singular and $plural for count$count from domain $domain.
The context is a unique identifier for the translations string that makes it unique within the same domain.
Note that the category must be specified with an I18n class constant, instead of only the constant name. The
values are:• I18n::LC_ALL - LC_ALL• I18n::LC_COLLATE - LC_COLLATE• I18n::LC_CTYPE - LC_CTYPE• I18n::LC_MONETARY - LC_MONETARY• I18n::LC_NUMERIC - LC_NUMERIC• I18n::LC_TIME - LC_TIME• I18n::LC_MESSAGES - LC_MESSAGES__n(string $singular, string $plural, integer $count, mixed $args = null)
Returns correct plural form of message identified by $singular and $plural for count $count. Some languages
have more than one form for plural messages dependent on the count.
am(array $one, $two, $three...)Merges all the arrays passed as parameters and returns the merged array.
config()Can be used to load files from your application config-folder via include_once. Function checks for existence
before include and returns boolean. Takes an optional number of arguments.
Example: config('some_file', 'myconfig');
convertSlash(string $string)
Converts forward slashes to underscores and removes the first and last underscores in a string. Returns theconverted string.

336

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

debug(mixed $var, boolean $showHtml = null, $showFrom = true)
If the application’s DEBUG level is non-zero, $var is printed out. If $showHTML is true or left as null, the data isrendered to be browser-friendly. If $showFrom is not set to false, the debug output will start with the line fromwhich it was called. Also see Debugging
stackTrace(array $options = array())
If the application’s DEBUG level is non-zero, the stack trace is printed out.
env(string $key)
Gets an environment variable from available sources. Used as a backup if $_SERVER or $_ENV are disabled.
This function also emulates PHP_SELF and DOCUMENT_ROOT on unsupporting servers. In fact, it’s a goodidea to always use env() instead of $_SERVER or getenv() (especially if you plan to distribute the code), since
it’s a full emulation wrapper.
fileExistsInPath(string $file)
Checks to make sure that the supplied file is within the current PHP include_path. Returns a boolean result.
h(string $text, boolean $double = true, string $charset = null)Convenience wrapper for htmlspecialchars().
LogError(string $message)
Shortcut to Log::write().
pluginSplit(string $name, boolean $dotAppend = false, string $plugin = null)
Splits a dot syntax plugin name into its plugin and class name. If $name does not have a dot, then index 0 willbe null.
Commonly used like list($plugin, $name) = pluginSplit('Users.User');pr(mixed $var)
Convenience wrapper for print_r(), with the addition of wrapping <pre> tags around the output.
sortByKey(array &$array, string $sortby, string $order = 'asc', integer $type = SORT_NUMERIC)
Sorts given $array by key $sortby.
stripslashes_deep(array $value)
Recursively strips slashes from the supplied $value. Returns the modified array.
Core Definition Constants
Most of the following constants refer to paths in your application.
constant APP
Absolute path to your application directory, including a trailing slash.
constant APP_DIR
Equals app or the name of your application directory.
constant APPLIBSPath to the application’s Lib directory.
constant CACHE
Path to the cache files directory. It can be shared between hosts in a multi-server setup.
constant CAKE
Path to the cake directory.

General Purpose

337

CakePHP Cookbook Documentation, Release 2.x

constant CAKE_CORE_INCLUDE_PATHPath to the root lib directory.
constant CONFIG
Path to the app/Config directory.
New in version 2.10.0.
constant CORE_PATHPath to the root directory with ending directory slash.
constant CSS
Path to the public CSS directory.
Deprecated since version 2.4.
constant CSS_URL
Web path to the CSS files directory.
Deprecated since version 2.4: Use config value App.cssBaseUrl instead.
constant DS
Short for PHP’s DIRECTORY_SEPARATOR, which is / on Linux and \ on Windows.
constant FULL_BASE_URL
Full URL prefix. Such as https://example.com
Deprecated since version 2.4: This constant is deprecated, you should use Router::fullBaseUrl() instead.
constant IMAGES
Path to the public images directory.
Deprecated since version 2.4.
constant IMAGES_URL
Web path to the public images directory.
Deprecated since version 2.4: Use config value App.imageBaseUrl instead.
constant JS
Path to the public JavaScript directory.
Deprecated since version 2.4.
constant JS_URL
Web path to the js files directory.
Deprecated since version 2.4: Use config value App.jsBaseUrl instead.
constant LOGS
Path to the logs directory.
constant ROOT
Path to the root directory.
constant TESTSPath to the tests directory.
constant TMP
Path to the temporary files directory.

338

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

constant VENDORSPath to the vendors directory.
constant WEBROOT_DIR
Equals webroot or the name of your webroot directory.
WWW\_ROOT
Full path to the webroot.
Timing Definition Constants
constant TIME_START
Unix timestamp in microseconds as a float from when the application started.
constant SECOND
Equals 1
constant MINUTE
Equals 60
constant HOUR
Equals 3600
constant DAY
Equals 86400
constant WEEK
Equals 604800
constant MONTH
Equals 2592000
constant YEAR
Equals 31536000
App Classclass AppThe app class is responsible for path management, class location and class loading. Make sure you follow the File andClass Name Conventions.
Packages
CakePHP is organized around the idea of packages, each class belongs to a package or folder where other classes
reside. You can configure each package location in your application using App::build('APackage/SubPackage',$paths) to inform the framework where should each class be loaded. Almost every class in the CakePHP framework
can be swapped with your own compatible implementation. If you wish to use your own class instead of the classes theframework provides, just add the class to your libs folder emulating the directory location of where CakePHP expects
to find it.
For instance if you’d like to use your own HttpSocket class, put it under:

General Purpose339

CakePHP Cookbook Documentation, Release 2.x

app/Lib/Network/Http/HttpSocket.php
Once you’ve done this App will load your override file instead of the file inside CakePHP.
Loading classes
static App::uses(string $class, string $package)
Return typevoid
Classes are lazily loaded in CakePHP, however before the autoloader can find your classes you need to tell App,
where it can find the files. By telling App which package a class can be found in, it can properly locate the fileand load it the first time a class is used.
Some examples for common types of classes are:Console Commands
App::uses('AppShell', 'Console/Command');
Console Tasks
App::uses('BakeTask', 'Console/Command/Task');
Controllers
App::uses('PostsController', 'Controller');
Components
App::uses('AuthComponent', 'Controller/Component');
ModelsApp::uses('MyModel', 'Model');Behaviors
App::uses('TreeBehavior', 'Model/Behavior');
Views
App::uses('ThemeView', 'View');
Helpers
App::uses('HtmlHelper', 'View/Helper');
Libs
App::uses('PaymentProcessor', 'Lib');
Vendors
App::uses('Textile', 'Vendor');Utilities
App::uses('CakeText', 'Utility');
So basically the second param should simply match the folder path of the class file in core or app.
Note: Loading vendors usually means you are loading packages that do not follow conventions. For most vendor
packages using App::import() is recommended.

340

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Loading files from plugins
Loading classes in plugins works much the same as loading app and core classes except you must specify the plugin
you are loading from:// Load the class Comment in app/Plugin/PluginName/Model/Comment.php
App::uses('Comment', 'PluginName.Model');// Load the class CommentComponent in// app/Plugin/PluginName/Controller/Component/CommentComponent.php
App::uses('CommentComponent', 'PluginName.Controller/Component');

Finding paths to packages using App::path()static App::path(string $package, string $plugin = null)
Return typearray
Used to read information stored path:// return the model paths in your application
App::path('Model');This can be done for all packages that are apart of your application. You can also fetch paths for a plugin:// return the component paths in DebugKit
App::path('Component', 'DebugKit');static App::paths()
Return typearray
Get all the currently loaded paths from App. Useful for inspecting or storing all paths App knows about. For apaths to a specific package use App::path()static App::core(string $package)
Return typearray
Used for finding the path to a package inside CakePHP:// Get the path to Cache engines.
App::core('Cache/Engine');static App::location(string $className)
Return typestringReturns the package name where a class was defined to be located at.

General Purpose341

CakePHP Cookbook Documentation, Release 2.x

Adding paths for App to find packages instatic App::build(array $paths = array(), mixed $mode = App::PREPEND)
Return type
void
Sets up each package location on the file system. You can configure multiple search paths for each package,
those will be used to look for files one folder at a time in the specified order. All paths must be terminated witha directory separator.
Adding additional controller paths for example would alter where CakePHP looks for controllers. This allows
you to split your application up across the filesystem.
Usage://will setup a new search path for the Model package
App::build(array('Model' => array('/a/full/path/to/models/')));//will setup the path as the only valid path for searching models
App::build(array('Model' => array('/path/to/models/')), App::RESET);//will setup multiple search paths for helpers
App::build(array('View/Helper' => array('/path/to/helpers/', '/another/path/')));If reset is set to true, all loaded plugins will be forgotten and they will be needed to be loaded again.
Examples:App::build(array('controllers' => array('/full/path/to/controllers/')));//becomes
App::build(array('Controller' => array('/full/path/to/Controller/')));
App::build(array('helpers' => array('/full/path/to/views/helpers/')));//becomes
App::build(array('View/Helper' => array('/full/path/to/View/Helper/')));
Changed in version 2.0: App::build() will not merge app paths with core paths anymore.
Add new packages to an application
App::build() can be used to add new package locations. This is useful when you want to add new top level packages
or, sub-packages to your application:
App::build(array('Service' => array('%s' . 'Service' . DS)), App::REGISTER);
The %s in newly registered packages will be replaced with the APP path. You must include a trailing / in registeredpackages. Once packages are registered, you can use App::build() to append/prepend/reset paths like any otherpackage.
Changed in version 2.1: Registering packages was added in 2.1

342

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Finding which objects CakePHP knows about
static App::objects(string $type, mixed $path = null, boolean $cache = true)
Return typemixed Returns an array of objects of the given type or false if
incorrect.
You can find out which objects App knows about using App::objects('Controller') for example to findwhich application controllers App knows about.
Example usage://returns array('DebugKit', 'Blog', 'User');
App::objects('plugin');//returns array('PagesController', 'BlogController');
App::objects('Controller');
You can also search only within a plugin’s objects by using the plugin dot syntax.
// returns array('MyPluginPost', 'MyPluginComment');
App::objects('MyPlugin.Model');Changed in version 2.0.
1. Returns array() instead of false for empty results or invalid types
2. Does not return core objects anymore, App::objects('core') will return array().
3. Returns the complete class nameLocating plugins
static App::pluginPath(string $plugin)
Return typestringPlugins can be located with App as well. Using App::pluginPath('DebugKit'); for example, will give you
the full path to the DebugKit plugin:$path = App::pluginPath('DebugKit');

Locating themes
static App::themePath(string $theme)
Return typestring
Themes can be found App::themePath('purple');, would give the full path to the purple theme.

General Purpose343

CakePHP Cookbook Documentation, Release 2.x

Including files with App::import()
static App::import(mixed $type = null, string $name = null, mixed $parent = true, array $search = array(),
string $file = null, boolean $return = false)
Return typebooleanAt first glance App::import seems complex, however in most use cases only 2 arguments are required.
Note: This method is equivalent to require’ing the file. It is important to realize that the class subsequently
needs to be initialized.
// The same as require('Controller/UsersController.php');
App::import('Controller', 'Users');// We need to load the class$Users = new UsersController();// If we want the model associations, components, etc to be loaded$Users->constructClasses();
All classes that were loaded in the past using App::import(‘Core’, $class) will need to be loaded using
App::uses() referring to the correct package. This change has provided large performance gains to theframework.
Changed in version 2.0.
• The method no longer looks for classes recursively, it strictly uses the values for the paths defined in
App::build()
• It
will
not
be
able
to
load
App::import('Component', 'Component')
App::uses('Component', 'Controller');.

use• Using App::import('Lib', 'CoreClass'); to load core classes is no longer possible.
• Importing a non-existent file, supplying a wrong type or package name, or null values for $name and $fileparameters will result in a false return value.
• App::import('Core', 'CoreClass') is no longer supported, use App::uses() instead and let theclass autoloading do the rest.
• Loading Vendor files does not look recursively in the vendors folder, it will also not convert the file tounderscored anymore as it did in the past.
Overriding classes in CakePHPYou can override almost every class in the framework, exceptions are the App and Configure classes. Whenever you
like to perform such overriding, just add your class to your app/Lib folder mimicking the internal structure of theframework. Some examples to follow:• To override the Dispatcher class, create app/Lib/Routing/Dispatcher.php• To override the CakeRoute class, create app/Lib/Routing/Route/CakeRoute.php• To override the Model class, create app/Lib/Model/Model.php
When you load the overridden classes now, the files in app/Lib will be loaded instead of the built-in core ones.
344

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Loading Vendor Files
You can use App::uses() to load classes in vendors directories. It follows the same conventions as loading other files:// Load the class Geshi in app/Vendor/Geshi.php
App::uses('Geshi', 'Vendor');To load classes in subdirectories, you’ll need to add those paths with App::build():// Load the class ClassInSomePackage in// app/Vendor/SomePackage/ClassInSomePackage.php
App::build(array('Vendor' => array(APP . 'Vendor' . DS . 'SomePackage' . DS)));
App::uses('ClassInSomePackage', 'Vendor');Your vendor files may not follow conventions, have a class that differs from the file name or does not contain classes.
You can load those files using App::import(). The following examples illustrate how to load vendor files from anumber of path structures. These vendor files could be located in any of the vendor folders.
To load app/Vendor/geshi.php:
App::import('Vendor', 'geshi');Note: The geshi file must be a lower-case file name as CakePHP will not find it otherwise.
To load app/Vendor/flickr/flickr.php:
App::import('Vendor', 'flickr', array('file' => 'flickr/flickr.php'));To load app/Vendor/some.name.php:
App::import('Vendor', 'SomeName', array('file' => 'some.name.php'));To load app/Vendor/services/well.named.php:
App::import('Vendor','WellNamed',array('file' => 'services' . DS . 'well.named.php'));
To load app/Plugin/Awesome/Vendor/services/well.named.php:
App::import('Vendor','Awesome.WellNamed',array('file' => 'services' . DS . 'well.named.php'));
To load app/Plugin/Awesome/Vendor/Folder/Foo.php:
App::import('Vendor','Awesome.Foo',array('file' => 'Folder' . DS . 'Foo.php'));General Purpose

345

CakePHP Cookbook Documentation, Release 2.x

It wouldn’t make a difference if your vendor files are inside your /vendors directory. CakePHP will automatically findit.
To load vendors/vendorName/libFile.php:
App::import('Vendor','aUniqueIdentifier',array('file' => 'vendorName' . DS . 'libFile.php'));

App Init/Load/Shutdown Methods
static App::init()
Return type
void
Initializes the cache for App, registers a shutdown function.
static App::load(string $className)
Return typeboolean
Method to handle the automatic class loading. It will look for each class’ package defined using App::uses()
and with this information it will resolve the package name to a full path to load the class from. File name foreach class should follow the class name. For instance, if a class is name MyCustomClass the file name should
be MyCustomClass.php
static App::shutdown()
Return type
void
Object destructor. Writes cache file if changes have been made to the $_map.
Events System
New in version 2.1.
Creating maintainable applications is both a science and an art. It is well-known that a key for having good quality
code is making your objects loosely coupled and strongly cohesive at the same time. Cohesion means that all methods
and properties for a class are strongly related to the class itself and it is not trying to do the job other objects should
be doing, while loosely coupling is the measure of how little a class is “wired” to external objects, and how much thatclass is depending on them.
There are certain cases where you need to cleanly communicate with other parts of an application, without having tohard code dependencies, thus losing cohesion and increasing class coupling. Using the Observer pattern, which allows
objects to notify other objects and anonymous listeners about changes is a useful pattern to achieve this goal.
Listeners in the observer pattern can subscribe to events and choose to act upon them if they are relevant. If you haveused JavaScript, there is a good chance that you are already familiar with event driven programming.
CakePHP emulates several aspects of how events are triggered and managed in popular JavaScript libraries such as
jQuery. In the CakePHP implementation, an event object is dispatched to all listeners. The event object holds information about the event, and provides the ability to stop event propagation at any point. Listeners can register themselves

346

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

or can delegate this task to other objects and have the chance to alter the state and the event itself for the rest of thecallbacks.
The event subsystem is at the heart of Model, Behavior, Controller, View and Helper callbacks. If you’ve ever used anyof them, you are already somewhat familiar with events in CakePHP.
Example Event Usage
Let’s suppose you are building a Cart plugin, and you’d like to focus on just handling order logic. You don’t really want
to include shipping logic, emailing the user or decrementing the item from the stock, but these are important tasks tothe people using your plugin. If you were not using events, you may try to implement this by attaching behaviors tomodels, or adding components to your controllers. Doing so represents a challenge most of the time, since you would
have to come up with the code for externally loading those behaviors or attaching hooks to your plugin controllers.
Instead, you can use events to allow you to cleanly separate the concerns of your code and allow additional concerns tohook into your plugin using events. For example in your Cart plugin you have an Order model that deals with creatingorders. You’d like to notify the rest of the application that an order has been created. To keep your Order model clean
you could use events:// Cart/Model/Order.php
App::uses('CakeEvent', 'Event');class Order extends AppModel {public function place($order) {if ($this->save($order)) {$this->Cart->remove($order);$event = new CakeEvent('Model.Order.afterPlace', $this, array('order' => $order));$this->getEventManager()->dispatch($event);
return true;}
return false;}}
The above code allows you to easily notify the other parts of the application that an order has been created. You canthen do tasks like send email notifications, update stock, log relevant statistics and other tasks in separate objects that
focus on those concerns.
Accessing Event Managers
In CakePHP events are triggered against event managers. Event managers are available in every Model, View andController using getEventManager():$events = $this->getEventManager();
Each model has a separate event manager, while the View and Controller share one. This allows model events to beself contained, and allow components or controllers to act upon events created in the view if necessary.

General Purpose

347

CakePHP Cookbook Documentation, Release 2.x

Global Event Manager
In addition to instance level event managers, CakePHP provides a global event manager that allows you to listen to any
event fired in an application. This is useful when attaching listeners to a specific instance might be cumbersome ordifficult. The global manager is a singleton instance of CakeEventManager. When an event is dispatched, it will bedispatched both to the global and to the instance level listeners in priority order. You can access the global manager
using a static method:// In any configuration file or piece of code that executes before the event
App::uses('CakeEventManager', 'Event');
CakeEventManager::instance()->attach($aCallback,'Model.Order.afterPlace');
One important thing you should consider is that there are events that will be triggered having the same name but different
subjects, so checking it in the event object is usually required in any function that gets attached globally in order toprevent some bugs. Remember that with the flexibility of using the global manager, some additional complexity is
incurred.
Changed in version 2.5: Prior to 2.5, listeners on the global manager were kept in a separate list and fired before
instance listeners are. After 2.5, global and instance listeners are fired in priority order.
Dispatching Events
Once you have obtained an instance of an event manager you can dispatch events using dispatch(). This method
takes an instance of the CakeEvent class. Let’s look at dispatching an event:// Create a new event and dispatch it.
$event = new CakeEvent('Model.Order.afterPlace', $this, array('order' => $order));$this->getEventManager()->dispatch($event);
CakeEvent accepts 3 arguments in its constructor. The first one is the event name, you should try to keep this name as
unique as possible, while making it readable. We suggest a convention as follows: Layer.eventName for general events
happening at a layer level (e.g. Controller.startup, View.beforeRender) and Layer.Class.eventName for
events happening in specific classes on a layer, for example Model.User.afterRegister or Controller.Courses.
invalidAccess.
The second argument is the subject, meaning the object associated to the event, usually when it is the same classtriggering events about itself, using $this will be the most common case. Although a Component could trigger
controller events too. The subject class is important because listeners will get immediate access to the object properties
and have the chance to inspect or change them on the fly.
Finally, the third argument is any additional event data.This can be any data you consider useful to pass around solisteners can act upon it. While this can be an argument of any type, we recommend passing an associative array.
The dispatch() method accepts an event object as an argument and notifies all subscribed listeners.

348

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Registering Listeners
Listeners are the preferred way to register callbacks for an event.
This is done by implementing theCakeEventListener interface in any class you wish to register some callbacks. Classes implementing it need toprovide the implementedEvents() method. This method must return an associative array with all event names thatthe class will handle.
To continue our previous example, let’s imagine we have a UserStatistic class responsible for calculating a user’spurchasing history, and compiling into global site statistics. This is a great place to use a listener class. Doing soallows you concentrate the statistics logic in one place and react to events as necessary. Our UserStatistics listenermight start out like:// In app/Lib/Event/UserStatistic.php
App::uses('CakeEventListener', 'Event');class UserStatistic implements CakeEventListener {public function implementedEvents() {return array('Model.Order.afterPlace' => 'updateBuyStatistic',);}
public function updateBuyStatistic($event) {// Code to update statistics}}// In a controller or somewhere else where $this->Order is accessible// Attach the UserStatistic object to the Order's event manager$statistics = new UserStatistic();$this->Order->getEventManager()->attach($statistics);
As you can see in the above code, the attach function will accept instances of the CakeEventListener interface.
Internally, the event manager will use implementedEvents to attach the correct callbacks.
Registering Global Listeners
As shown in the example above, event listeners are conventionally placed in app/Lib/Event. Following this convention allows you to easily locate your listener classes. It is also recommended that you attach global listeners during yourapplication bootstrap process:// In app/Config/bootstrap.php// Load the global event listeners.
require_once APP . 'Config' . DS . 'events.php'
An example events bootstrap file for our cart application could look like:// In app/Config/events.php// Load event listeners
App::uses('UserStatistic', 'Lib/Event');(continues on next page)

General Purpose349

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

App::uses('ProductStatistic', 'Lib/Event');
App::uses('CakeEventManager', 'Event');// Attach listeners.
CakeEventManager::instance()->attach(new UserStatistic());
CakeEventManager::instance()->attach(new ProductStatistic());

Registering Anonymous Listeners
While event listener objects are generally a better way to implement listeners, you can also bind any callable as an
event listener. For example if we wanted to put any orders into the log files, we could use a simple anonymous function
to do so:// Anonymous functions require PHP 5.3+
$this->Order->getEventManager()->attach(function($event) {CakeLog::write('info','A new order was placed with id: ' . $event->subject()->id);}, 'Model.Order.afterPlace');
In addition to anonymous functions you can use any other callable type that PHP supports:$events = array('email-sending' => 'EmailSender::sendBuyEmail','inventory' => array($this->InventoryManager, 'decrement'),);
foreach ($events as $callable) {$eventManager->attach($callable, 'Model.Order.afterPlace');}

Establishing Priorities
In some cases you might want to control the order that listeners are invoked. For instance, if we go back to our user
statistics example. It would be ideal if this listener was called at the end of the stack. By calling it at the end of thelistener stack, we can ensure that the event was not canceled, and that no other listeners raised exceptions. We can also
get the final state of the objects in the case that other listeners have modified the subject or event object.
Priorities are defined as an integer when adding a listener. The higher the number, the later the method will be fired.
The default priority for all listeners is 10. If you need your method to be run earlier, using any value below this default
will work. On the other hand if you desire to run the callback after the others, using a number above 10 will do.
If two callbacks happen to have the same priority value, they will be executed with a the order they were attached. You
set priorities using the attach method for callbacks, and declaring it in the implementedEvents function for event
listeners:// Setting priority for a callback$callback = array($this, 'doSomething');$this->getEventManager()->attach($callback,'Model.Order.afterPlace',(continues on next page)

350

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)array('priority' => 2));// Setting priority for a listenerclass UserStatistic implements CakeEventListener {public function implementedEvents() {return array('Model.Order.afterPlace' => array('callable' => 'updateBuyStatistic','priority' => 100
),
);
}
}
As you see, the main difference for CakeEventListener objects is that you need to use an array for specifying thecallable method and the priority preference. The callable key is an special array entry that the manager will read toknow what function in the class it should be calling.
Getting Event Data as Function Parameters
By default listeners receive the event object as their only parameter. If you are building an event that doesn’t needaccess to the event object you may want to have the event data passed as function parameters. This feature is used by
the callbacks CakePHP fires in order to preserve backwards compatibility.
If you want to enable this feature, you have to add the passParams option to the third argument of the attach method,or declare it in the implementedEvents returned array similar to what you do with priorities:// Enabling passed parameters mode for an anonymous listener$callback = array($this, 'doSomething');$this->getEventManager()->attach($callback,'Model.Order.afterPlace',array('passParams' => true));// Enabling passed parameters mode for a listenerclass UserStatistic implements CakeEventListener {public function implementedEvents() {return array('Model.Order.afterPlace' => array('callable' => 'updateBuyStatistic','passParams' => true),);}
public function updateBuyStatistic($orderData) {// ...
}}

General Purpose351

CakePHP Cookbook Documentation, Release 2.x

In the above code the doSomething function and updateBuyStatistic method will receive $orderData instead of
the $event object. This is so, because in our previous example we trigger the Model.Order.afterPlace event withsome data:$event = new CakeEvent('Model.Order.afterPlace', $this, array('order' => $order));$this->getEventManager()->dispatch($event);

Note: The params can only be passed as function arguments if the event data is an array. Any other data type cannot
be converted to function parameters, thus not using this option is often the most adequate choice.

Stopping Events
Much like DOM events, you may want to stop an event to prevent additional listeners from being notified. You can see
this in action during model callbacks (e.g. beforeSave) in which it is possible to stop the saving operation if the code
detects it cannot proceed any further.
In order to stop events you can either return false in your callbacks or call the stopPropagation method on theevent object:public function doSomething($event) {// ...
return false; // stops the event}
public function updateBuyStatistic($event) {// ...
$event->stopPropagation();}Stopping an event will prevent any additional callbacks from being called. Additionally the code triggering the event
may behave differently based on the event being stopped or not. Generally it does not make sense to stop ‘after’ events,
but stopping ‘before’ events is often used to prevent the entire operation from occurring.
To check if an event was stopped, you call the isStopped() method in the event object:public function place($order) {$event = new CakeEvent('Model.Order.beforePlace',$this, array('order' => $order));$this->getEventManager()->dispatch($event);
if ($event->isStopped()) {return false;}
if ($this->Order->save($order)) {// ...
}// ...
}In the previous example the order would not get saved if the event is stopped during the beforePlace process.
352

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Getting Event Results
Every time a callback returns a value, it gets stored in the $result property of the event object. This is useful when you
want to allow callbacks to modify the event execution. Let’s take again our beforePlace example and let callbacks
modify the $order data.
Event results can be altered either using the event object result property directly or returning the value in the callback
itself:// A listener callback
public function doSomething($event) {// ...
$alteredData = $event->data['order'] + $moreData;
return $alteredData;}// Another listener callback
public function doSomethingElse($event) {// ...
$event->result['order'] = $alteredData;}// Using the event result
public function place($order) {$event = new CakeEvent('Model.Order.beforePlace',$this, array('order' => $order));$this->getEventManager()->dispatch($event);
if (!empty($event->result['order'])) {$order = $event->result['order'];}
if ($this->Order->save($order)) {// ...
}// ...
}
It is possible to alter any event object property and have the new data passed to the next callback. In most of the cases,
providing objects as event data or result and directly altering the object is the best solution as the reference is kept thesame and modifications are shared across all callback calls.
Removing Callbacks and Listeners
If for any reason you want to remove any callback from the event manager just call the CakeEventManager::detach()method using as arguments the first two params you used for attaching it:// Attaching a function$this->getEventManager()->attach(array($this, 'doSomething'), 'My.event');// Detaching the function$this->getEventManager()->detach(array($this, 'doSomething'), 'My.event');(continues on next page)

General Purpose353

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// Attaching an anonymous function (PHP 5.3+ only);$myFunction = function($event) { ... };$this->getEventManager()->attach($myFunction, 'My.event');// Detaching the anonymous function$this->getEventManager()->detach($myFunction, 'My.event');// Attaching a CakeEventListener$listener = new MyEventListener();$this->getEventManager()->attach($listener);// Detaching a single event key from a listener$this->getEventManager()->detach($listener, 'My.event');// Detaching all callbacks implemented by a listener$this->getEventManager()->detach($listener);

Conclusion
Events are a great way of separating concerns in your application and make classes both cohesive and decoupled fromeach other. Events can be utilized to de-couple application code and make extensible plugins.
Keep in mind that with great power comes great responsibility. Using too many events can make debugging harder andrequire additional integration testing.
Additional Reading
Collections
Components, Helpers, Behaviors and Tasks all share a similar structure and set of behaviors. For 2.0, they were givena unified API for interacting with collections of similar objects. The collection objects in CakePHP, give you a uniform
way to interact with several different kinds of objects in your application.
While the examples below, will use Components, the same behavior can be expected for Helpers, Behaviors, and Tasks
in addition to Components.
Loading and unloading objects
Loading objects on every kind of collection can be done using the load() method:$this->Prg = $this->Components->load('Prg');$this->Prg->process();
When loading a component, if the component is not currently loaded into the collection, a new instance will be created.
If the component is already loaded, another instance will not be created. When loading components, you can also
provide additional configuration for them:$this->Cookie = $this->Components->load('Cookie', array('name' => 'sweet'));354

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Any keys & values provided will be passed to the Component’s constructor. The one exception to this rule isclassName. ClassName is a special key that is used to alias objects in a collection. This allows you to have component names that do not reflect the classnames, which can be helpful when extending core components:$this->Auth = $this->Components->load('Auth',
array('className' => 'MyCustomAuth'));$this->Auth->user(); // Actually using MyCustomAuth::user();
The inverse of loading an object, is unloading it. Unloaded objects are removed from memory, and will not haveadditional callbacks triggered on them:$this->Components->unload('Cookie');$this->Cookie->read(); // Fatal error.

Triggering callbacks
Callbacks are supported by collection objects. When a collection has a callback triggered, that method will be calledon all enabled objects in the collection. You can pass parameters to the callback loop as well:$this->Behaviors->trigger('afterFind', array($this, $results, $primary));
In the above $this would be passed as the first argument to every behavior’s afterFind method. There are several
options that can be used to control how callbacks are fired:• breakOn Set to the value or values you want the callback propagation to stop on. Can either be a scalar value,or an array of values to break on. Defaults to false.
• break Set to true to enabled breaking. When a trigger is broken, the last returned value will be returned. If usedin combination with collectReturn the collected results will be returned. Defaults to false.
• collectReturn Set to true to collect the return of each object into an array. This array of return values will be
returned from the trigger() call. Defaults to false.
• triggerDisabled Will trigger the callback on all objects in the collection even the non-enabled objects. Defaults to false.
• modParams Allows each object the callback gets called on to modify the parameters to the next object. Setting
modParams to an integer value will allow you to modify the parameter with that index. Any non-null value will
modify the parameter index indicated. Defaults to false.
Canceling a callback loop
Using the break and breakOn options you can cancel a callback loop midway similar to stopping event propagationin JavaScript:$this->Behaviors->trigger('beforeFind',array($this, $query),array('break' => true, 'breakOn' => false));
In the above example, if any behavior returns false from its beforeFind method, no further callbacks will be called.
In addition, the return of trigger() will be false.
General Purpose

355

CakePHP Cookbook Documentation, Release 2.x

Enabling and disabling objects
Once an object is loaded into a collection you may need to disable it. Disabling an object in a collection prevents future
callbacks from being fired on that object unless the triggerDisabled option is used:// Disable the HtmlHelper$this->Helpers->disable('Html');// Re-enable the helper later on$this->Helpers->enable('Html');
Disabled objects can still have their normal methods and properties used. The primary difference between an enabled
and disabled object is with regards to callbacks. You can interrogate a collection about the enabled objects, or check if
a specific object is still enabled using enabled():// Check whether or not a specific helper is enabled.
$this->Helpers->enabled('Html');// $enabled will contain an array of helper currently enabled.
$enabled = $this->Helpers->enabled();Object callback priorities
You can prioritize the triggering object callbacks similar to event callbacks. The handling of priority values and orderof triggering is the same as explained here. Here’s how you can specify priority at declaration time:class SomeController {public $components = array('Foo', //Foo gets default priority 10// Bar's callbacks are triggered before Foo's'Bar' => array('priority' => 9));
public $helpers = array(// Cache's callbacks will be triggered last
'Cache' => array('priority' => 12),'Asset','Utility' //Utility has priority 10 same as Asset and its callbacks//are triggered after Asset's);}

class Post {public $actsAs = array('DoFirst' => array('priority' => 1),'Media');}When dynamically loading objects to a collection you can specify the priority like this:356

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x$this->MyComponent = $this->Components->load('MyComponent',array('priority' => 9));You can also change priorities at run time using the ObjectCollection::setPriority() function://For a single object$this->Components->setPriority('Foo', 2);//For multiple objects$this->Behaviors->setPriority(array('Object1' => 8, 'Object2' => 9));Behaviors
Behaviors add extra functionality to your models. CakePHP comes with a number of built-in behaviors such as
TreeBehavior and ContainableBehavior.
To learn about creating and using behaviors, read the section on Behaviors.

Behaviors
Behaviors add extra functionality to your models. CakePHP comes with a number of built-in behaviors such as
TreeBehavior and ContainableBehavior.
To learn about creating and using behaviors, read the section on Behaviors.

Components
CakePHP has a selection of components to help take care of basic tasks in your controllers. See the section on Components for how to configure and use components.

Components
CakePHP has a selection of components to help take care of basic tasks in your controllers. See the section on Components for how to configure and use components.
Pagination
class PaginatorComponent(ComponentCollection $collection, array $settings = array())
One of the main obstacles of creating flexible and user-friendly web applications is designing an intuitive user interface.
Many applications tend to grow in size and complexity quickly, and designers and programmers alike find they areunable to cope with displaying hundreds or thousands of records. Refactoring takes time, and performance and user
satisfaction can suffer.
Displaying a reasonable number of records per page has always been a critical part of every application and used tocause many headaches for developers. CakePHP eases the burden on the developer by providing a quick, easy way topaginate data.
Behaviors

357

CakePHP Cookbook Documentation, Release 2.x

Pagination in CakePHP is offered by a component in the controller, to make building paginated queries easier. In the
View PaginatorHelper is used to make the generation of pagination links & buttons simple.
Query Setup
In the controller, we start by defining the query conditions pagination will use by default in the $paginate controller
variable. These conditions, serve as the basis of your pagination queries. They are augmented by the sort, direction,
limit, and page parameters passed in from the URL. It is important to note here that the order key must be definedin an array structure like below:
class PostsController extends AppController {public $components = array('Paginator');public $paginate = array('limit' => 25,'order' => array('Post.title' => 'asc'));}
You can also include other find() options, such as fields:class PostsController extends AppController {public $components = array('Paginator');public $paginate = array('fields' => array('Post.id', 'Post.created'),'limit' => 25,'order' => array('Post.title' => 'asc'));}
Other keys that can be included in the $paginate array are similar to the parameters of the Model->find('all')method, that is: conditions, fields, order, limit, page, contain, joins, and recursive. In addition to theaforementioned keys, any additional keys will also be passed directly to the model find methods. This makes it very
simple to use behaviors like ContainableBehavior with pagination:
class RecipesController extends AppController {public $components = array('Paginator');public $paginate = array('limit' => 25,'contain' => array('Article'));}
In addition to defining general pagination values, you can define more than one set of pagination defaults in the controller, you just name the keys of the array after the model you wish to configure:
358

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

class PostsController extends AppController {public $paginate = array('Post' => array (...),'Author' => array (...));}
The values of the Post and Author keys could contain all the properties that a model/key less $paginate array could.
Once the $paginate variable has been defined, we can use the PaginatorComponent’s paginate() method fromour controller action. This will return find() results from the model. It also sets some additional paging parameters,
which are added to the request object. The additional information is set to $this->request->params['paging'],
and is used by PaginatorHelper for creating links.
PaginatorComponent::paginate() also adds
PaginatorHelper to the list of helpers in your controller, if it has not been added already:public function list_recipes() {$this->Paginator->settings = $this->paginate;// similar to findAll(), but fetches paged results$data = $this->Paginator->paginate('Recipe');$this->set('data', $data);}
You can filter the records by passing conditions as second parameter to the paginate() function:$data = $this->Paginator->paginate('Recipe',array('Recipe.title LIKE' => 'a%'));
Or you can also set conditions and other pagination settings array inside your action:
public function list_recipes() {$this->Paginator->settings = array('conditions' => array('Recipe.title LIKE' => 'a%'),'limit' => 10);$data = $this->Paginator->paginate('Recipe');$this->set(compact('data'));}Custom Query Pagination
If you’re not able to use the standard find options to create the query you need to display your data, there are a few
options. You can use a custom find type. You can also implement the paginate() and paginateCount() methods
on your model, or include them in a behavior attached to your model. Behaviors implementing paginate and/or
paginateCount should implement the method signatures defined below with the normal additional first parameter of$model:// paginate and paginateCount implemented on a behavior.
public function paginate(Model $model, $conditions, $fields, $order, $limit,(continues on next page)

Components359

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$page = 1, $recursive = null, $extra = array()) {// method content}
public function paginateCount(Model $model, $conditions = null, $recursive = 0,
$extra = array()) {// method body}It’s seldom you’ll need to implement paginate() and paginateCount(). You should make sure you can’t achieve yourgoal with the core model methods, or a custom finder. To paginate with a custom find type, you should set the 0’thelement, or the findType key as of 2.3:
public $paginate = array('popular');Since the 0th index is difficult to manage, in 2.3 the findType option was added:
public $paginate = array('findType' => 'popular');
The paginate() method should implement the following method signature. To use your own method/logic override
it in the model you wish to get the data from:/*** Overridden paginate method - group by week, away_team_id and home_team_id*/
public function paginate($conditions, $fields, $order, $limit, $page = 1,
$recursive = null, $extra = array()) {$recursive = -1;$group = $fields = array('week', 'away_team_id', 'home_team_id');
return $this->find('all',
compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group'));}
You also need to override the core paginateCount(), this method expects the same arguments as
Model::find('count'). The example below uses some PostgresSQL-specifc features, so please adjust accordingly
depending on what database you are using:/*** Overridden paginateCount method*/
public function paginateCount($conditions = null, $recursive = 0,
$extra = array()) {$sql = "SELECTDISTINCT ON(
week, home_team_id, away_team_id)(continues on next page)360

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

week, home_team_id, away_team_id
FROM
games";$this->recursive = $recursive;$results = $this->query($sql);
return count($results);}
The observant reader will have noticed that the paginate method we’ve defined wasn’t actually necessary - All you haveto do is add the keyword in controller’s $paginate class variable:/*** Add GROUP BY clause*/public $paginate = array('MyModel' => array('limit' => 20,'order' => array('week' => 'desc'),'group' => array('week', 'home_team_id', 'away_team_id')));/*** Or on-the-fly from within the action*/
public function index() {$this->Paginator->settings = array('MyModel' => array('limit' => 20,'order' => array('week' => 'desc'),'group' => array('week', 'home_team_id', 'away_team_id')));}
In CakePHP 2.0, you no longer need to implement paginateCount() when using group clauses.
find('count') will correctly count the total number of rows.

The coreControl which fields used for ordering
By default sorting can be done with any column on a model. This is sometimes undesirable as it can allow users tosort on un-indexed columns, or virtual fields that can be expensive to calculate. You can use the 3rd parameter of
PaginatorComponent::paginate() to restrict the columns that sorting will be done on:$this->Paginator->paginate('Post', array(), array('title', 'slug'));This would allow sorting on the title and slug columns only. A user that sets sort to any other value will be ignored.

Components361

CakePHP Cookbook Documentation, Release 2.x

Limit the maximum number of rows per pageThe number of results that are fetched per page is exposed to the user as the limit parameter. It is generally undesirable
to allow users to fetch all rows in a paginated set. The maxLimit option asserts that no one can set this limit too highfrom the outside. By default CakePHP limits the maximum number of rows that can be fetched to 100. If this default is
not appropriate for your application, you can adjust it as part of the pagination options, for example reducing it to 10:
public $paginate = array(// other keys here.
'maxLimit' => 10);
If the request’s limit param is greater than this value, it will be reduced to the maxLimit value.
Pagination with GET parameters
In previous versions of CakePHP you could only generate pagination links using named parameters. But if pages
were requested with GET parameters they would still work. For 2.0, we decided to make how you generate pagination
parameters more controlled and consistent. You can choose to use either querystring or named parameters in thecomponent. Incoming requests will accept only the chosen type, and the PaginatorHelper will generate links withthe chosen type of parameter:
public $paginate = array('paramType' => 'querystring');
The above would enable querystring parameter parsing and generation. You can also modify the $settings propertyon the PaginatorComponent:$this->Paginator->settings['paramType'] = 'querystring';
By default all of the typical paging parameters will be converted into GET arguments.
Note: You can run into a situation where assigning a value to a nonexistent property will throw errors:$this->paginate['limit'] = 10;
will throw the error “Notice: Indirect modification of overloaded property $paginate has no effect.” Assigning an initialvalue to the property solves the issue:$this->paginate = array();$this->paginate['limit'] = 10;//or$this->paginate = array('limit' => 10);
Or just declare the property in the controller class:class PostsController {public $paginate = array();}
Or use $this->Paginator->settings = array('limit' => 10);362

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Make sure you have added the Paginator component to your $components array if you want to modify the $settings
property of the PaginatorComponent.
Either of these approaches will solve the notice errors.

Out of range page requests
As of 2.3 the PaginatorComponent will throw a NotFoundException when trying to access a non-existent page, i.e.
page number requested is greater than total page count.
So you could either let the normal error page be rendered or use a try catch block and take appropriate action when a
NotFoundException is caught:
public function index() {try {$this->Paginator->paginate();} catch (NotFoundException $e) {//Do something here like redirecting to first or last page.
//$this->request->params['paging'] will give you required info.
}}

AJAX Pagination
It’s very easy to incorporate AJAX functionality into pagination.
Using the JsHelper andRequestHandlerComponent you can easily add AJAX pagination to your application. See AJAX Pagination
for more information.
Pagination in the view
Check the PaginatorHelper documentation for how to create links for pagination navigation.
Flashclass FlashComponent(ComponentCollection $collection, array $config = array())
FlashComponent provides a way to set one-time notification messages to be displayed after processing a form or acknowledging data. CakePHP refers to these messages as “flash messages”. FlashComponent writes flash messages to$_SESSION, to be rendered in a View using FlashHelper.
The FlashComponent replaces the setFlash() method on SessionComponent and should be used instead of thatmethod.

Components363

CakePHP Cookbook Documentation, Release 2.x

Setting Flash Messages
FlashComponent provides two ways to set flash messages: its __call magic method and its set() method.
To use the default flash message handler, you can use the set() method:$this->Flash->set('This is a message');
New in version 2.10.0: Flash messages now stack. Successive calls to set() or __call() with the same key will
append the messages in the $_SESSION. If you want to keep the old behavior (one message even after consecutive
calls), set the clear option to true when setting messages.
To create custom Flash elements, FlashComponent’s __call magic method allows you use a method name that maps
to an element located under the app/View/Elements/Flash directory. By convention, camelcased methods will map
to the lowercased and underscored element name:// Uses app/View/Elements/Flash/success.ctp$this->Flash->success('This was successful');// Uses app/View/Elements/Flash/great_success.ctp$this->Flash->greatSuccess('This was greatly successful');FlashComponent’s __call and set() methods optionally take a second parameter, an array of options:• key Defaults to ‘flash’. The array key found under the ‘Flash’ key in the session.
• element Defaults to null, but will automatically be set when using the __call() magic method. The element
name to use for rendering.
• params An optional array of keys/values to make available as variables within an element.
• clear Set to true to remove any existing flash messages of the given key/element. (Added in 2.10.0).
An example of using these options:// In your Controller$this->Flash->success('The user has been saved', array('key' => 'positive','params' => array('name' => $user['User']['name'],'email' => $user['User']['email'])));// In your View<?php echo $this->Flash->render('positive') ?><!-- In app/View/Elements/Flash/success.ctp --><div id="flash-<?php echo h($key) ?>" class="message-info success"><?php echo h($message) ?>: <?php echo h($params['name']) ?>, <?php echo h($params[
˓→'email']) ?>.
</div>
If you are using the __call() magic method, the element option will always be replaced. In order to retrieve a
specific element from a plugin, you should set the plugin parameter. For example:// In your Controller$this->Flash->warning('My message', array('plugin' => 'PluginName'));364

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

The code above will use the warning.ctp element under plugins/PluginName/View/Elements/Flash for rendering
the flash message.
Note: By default, CakePHP does not escape the HTML in flash messages. If you are using any request or user data inyour flash messages, you should escape it with h when formatting your messages.
For more information about rendering your flash messages, please refer to the FlashHelper section.
Sessions
class SessionComponent(ComponentCollection $collection, array $settings = array())
The CakePHP SessionComponent provides a way to persist client data between page requests. It acts as a wrapper for$_SESSION as well as providing convenience methods for several $_SESSION related functions.
Sessions can be configured in a number of ways in CakePHP. For more information, you should see the Session configuration documentation.
Interacting with Session data
The Session component is used to interact with session information. It includes basic CRUD functions as well asfeatures for creating feedback messages to users.
It should be noted that Array structures can be created in the Session by using dot notation. So User.username wouldreference the following:array('User' => array('username' => 'clark-kent@dailyplanet.com'));
Dots are used to indicate nested arrays. This notation is used for all Session component methods wherever a name/key
is used.
SessionComponent::write($name, $value)
Write to the Session puts $value into $name. $name can be a dot separated array. For example:$this->Session->write('Person.eyeColor', 'Green');
This writes the value ‘Green’ to the session under Person => eyeColor.
SessionComponent::read($name)Returns the value at $name in the Session. If $name is null the entire session will be returned. E.g:
$green = $this->Session->read('Person.eyeColor');Retrieve the value Green from the session. Reading data that does not exist will return null.
SessionComponent::consume($name)Read and delete a value from the Session. This is useful when you want to combine reading and deleting values
in a single operation.
SessionComponent::check($name)
Used to check if a Session variable has been set. Returns true on existence and false on non-existence.

Components

365

CakePHP Cookbook Documentation, Release 2.x

SessionComponent::delete($name)
Clear the session data at $name. E.g:
$this->Session->delete('Person.eyeColor');Our session data no longer has the value ‘Green’, or the index eyeColor set. However, Person is still in the Session.
To delete the entire Person information from the session use:
$this->Session->delete('Person');SessionComponent::destroy()
The destroy method will delete the session cookie and all session data stored in the temporary file system. It
will then destroy the PHP session and then create a fresh session:$this->Session->destroy();

Creating notification messages
SessionComponent::setFlash(string $message, string $element = 'default', array $params = array(), string $key
= 'flash')
Deprecated since version 2.7.0: You should use Flash to create flash messages. The setFlash() method will be
removed in 3.0.0.
Often in web applications, you will need to display a one-time notification message to the user after processing
a form or acknowledging data. In CakePHP, these are referred to as “flash messages”. You can set flash message
with the SessionComponent and display them with the SessionHelper::flash(). To set a message, use
setFlash:// In the controller.
$this->Session->setFlash('Your stuff has been saved.');This will create a one-time message that can be displayed to the user, using the SessionHelper:// In the view.
echo $this->Session->flash();// The above will output.
<div id="flashMessage" class="message">
Your stuff has been saved.
</div>
You can use the additional parameters of setFlash() to create different kinds of flash messages. For example,
you may want error and positive notifications to look different from each other. CakePHP gives you a way to dothat. Using the $key parameter, you can store multiple messages, which can be output separately:// set a bad message.
$this->Session->setFlash('Something bad.', 'default', array(), 'bad');// set a good message.
$this->Session->setFlash('Something good.', 'default', array(), 'good');
In the view, these messages can be output and styled differently:

366

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x// in a view.
echo $this->Session->flash('good');echo $this->Session->flash('bad');
The $element parameter allows you to control the element (located in /app/View/Elements) in which themessage should be rendered. Within the element, the message is available as $message. First we set the flash in
our controller:$this->Session->setFlash('Something custom!', 'flash_custom');
Then we create the file app/View/Elements/flash_custom.ctp and build our custom flash element:<div id="myCustomFlash"><?php echo h($message); ?></div>$params allows you to pass additional view variables to the rendered layout. Parameters can be passed affecting
the rendered div. For example, adding “class” in the $params array will apply a class to the div output using$this->Session->flash() in your layout or view:$this->Session->setFlash('Example message text','default',
array('class' => 'example_class'));
The output from using $this->Session->flash() with the above example would be:<div id="flashMessage" class="example_class">Example message text</div>
To use an element from a plugin just specify the plugin in the $params:// Will use /app/Plugin/Comment/View/Elements/flash_no_spam.ctp$this->Session->setFlash('Message!','flash_no_spam',array('plugin' => 'Comment'));

Note: By default CakePHP does not escape the HTML in flash messages. If you are using any request or user
data in your flash messages, you should escape it with h when formatting your messages.

Authentication
class AuthComponent(ComponentCollection $collection, array $settings = array())
Identifying, authenticating and authorizing users is a common part of almost every web application. In CakePHP
AuthComponent provides a pluggable way to do these tasks. AuthComponent allows you to combine authentication
objects, and authorization objects to create flexible ways of identifying and checking user authorization.

Components367

CakePHP Cookbook Documentation, Release 2.x

Suggested Reading Before Continuing
Configuring authentication requires several steps including defining a users table, creating a model, controller & views,
etc.
This is all covered step by step in the Blog Tutorial.
Authentication
Authentication is the process of identifying users by provided credentials and ensuring that users are who they say
they are. Generally this is done through a username and password, that are checked against a known list of users. In
CakePHP, there are several built-in ways of authenticating users stored in your application.
• FormAuthenticate allows you to authenticate users based on form POST data. Usually this is a login form thatusers enter information into.
• BasicAuthenticate allows you to authenticate users using Basic HTTP authentication.
• DigestAuthenticate allows you to authenticate users using Digest HTTP authentication.
By default AuthComponent uses FormAuthenticate.
Choosing an Authentication type
Generally you’ll want to offer form based authentication. It is the easiest for users using a web-browser to use. If
you are building an API or webservice, you may want to consider basic authentication or digest authentication. The
key differences between digest and basic authentication are mostly related to how passwords are handled. In basic
authentication, the username and password are transmitted as plain-text to the server. This makes basic authentication
un-suitable for applications without SSL, as you would end up exposing sensitive passwords. Digest authentication uses
a digest hash of the username, password, and a few other details. This makes digest authentication more appropriate
for applications without SSL encryption.
You can also use authentication systems like openid as well, however openid is not part of CakePHP core.
Configuring Authentication handlers
You configure authentication handlers using $this->Auth->authenticate. You can configure one or many handlers
for authentication. Using multiple handlers allows you to support different ways of logging users in. When logging
users in, authentication handlers are checked in the order they are declared. Once one handler is able to identify theuser, no other handlers will be checked. Conversely you can halt all authentication by throwing an exception. You willneed to catch any thrown exceptions, and handle them as needed.
You can configure authentication handlers in your controller’s beforeFilter or, in the $components array. You canpass configuration information into each authentication object, using an array:// Basic setup$this->Auth->authenticate = array('Form');// Pass settings in$this->Auth->authenticate = array('Basic' => array('userModel' => 'Member'),'Form' => array('userModel' => 'Member'));

368

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

In the second example you’ll notice that we had to declare the userModel key twice. To help you keep your code DRY,
you can use the all key. This special key allows you to set settings that are passed to every attached object. The all
key is also exposed as AuthComponent::ALL:// Pass settings in using 'all'$this->Auth->authenticate = array(
AuthComponent::ALL => array('userModel' => 'Member'),'Basic','Form');In the above example, both Form and Basic will get the settings defined for the ‘all’ key. Any settings passed to a
specific authentication object will override the matching key in the ‘all’ key. The core authentication objects support
the following configuration keys.
• fields The fields to use to identify a user by.
• userModel The model name of the User, defaults to User.
• scope Additional conditions to use when looking up and authenticating users, i.e. array('User.is_active'=> 1).
• recursive The value of the recursive key passed to find(). Defaults to 0.
• contain Containable options for when the user record is loaded. If you want to use this option, you’ll need tomake sure your model has the containable behavior attached.
New in version 2.2.
• passwordHasher Password hasher class. Defaults to Simple.
New in version 2.4.
• userFields The list of fields to fetch from the userModel. This option is helpful when you have a wide user
table and do not need all the columns in the session. By default all fields are fetched.
New in version 2.6.
To configure different fields for user in $components array:// Pass settings in $components arraypublic $components = array('Auth' => array('authenticate' => array('Form' => array('fields' => array('username' => 'email')))));
Do not put other Auth configuration keys (like authError, loginAction etc) within the authenticate or Form element.
They should be at the same level as the authenticate key. The setup above with other Auth configuration should look
like:// Pass settings in $components arraypublic $components = array('Auth' => array('loginAction' => array((continues on next page)

Components369

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'controller' => 'users','action' => 'login','plugin' => 'users'),'authError' => 'Did you really think you are allowed to see that?','authenticate' => array('Form' => array('fields' => array('username' => 'my_user_model_username_field', //Default is 'username'␣
˓→in the userModel'password' => 'my_user_model_password_field' //Default is 'password'␣
˓→in the userModel
)
)
)
)
);
In addition to the common configuration, Basic authentication supports the following keys:• realm The realm being authenticated. Defaults to env('SERVER_NAME').
In addition to the common configuration Digest authentication supports the following keys:• realm The realm authentication is for, Defaults to the servername.
• nonce A nonce used for authentication. Defaults to uniqid().
• qop Defaults to auth, no other values are supported at this time.
• opaque A string that must be returned unchanged by clients. Defaults to md5($settings['realm'])
Identifying users and logging them inIn the past AuthComponent auto-magically logged users in. This was confusing for many people, and made using
AuthComponent a bit difficult at times. For 2.0, you’ll need to manually call $this->Auth->login() to log a userin.
When authenticating users, attached authentication objects are checked in the order they are attached. Once one ofthe objects can identify the user, no other objects are checked. A sample login function for working with a login form
could look like:public function login() {if ($this->request->is('post')) {// Important: Use login() without arguments! See warning below.
if ($this->Auth->login()) {return $this->redirect($this->Auth->redirectUrl());// Prior to 2.3 use// `return $this->redirect($this->Auth->redirect());`}$this->Flash->error(__('Username or password is incorrect'));// Prior to 2.7 use// $this->Session->setFlash(__('Username or password is incorrect'));(continues on next page)

370

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}}The above code (without any data passed to the login method), will attempt to log a user in using the POST data, andif successful redirect the user to either the last page they were visiting, or AuthComponent::$loginRedirect. If thelogin is unsuccessful, a flash message is set.
Warning: In 2.x $this->Auth->login($this->request->data) will log the user in with whatever data isposted, whereas in 1.3 $this->Auth->login($this->data) would try to identify the user first and only log inwhen successful.

Using Digest and Basic Authentication for logging in
Because basic and digest authentication don’t require an initial POST or a form so if using only basic / digest authenticators you don’t require a login action in your controller. Also you can set AuthComponent::$sessionKey to false
to ensure AuthComponent doesn’t try to read user info from session. Stateless authentication will re-verify the user’s
credentials on each request, this creates a small amount of additional overhead, but allows clients that to login in without
using cookies.
Note: Prior to 2.4 you still need the login action as you are redirected to login when an unauthenticated user tries toaccess a protected page even when using only basic or digest auth. Also setting AuthComponent::$sessionKey tofalse will cause an error prior to 2.4.

Creating Custom Authentication objects
Because authentication objects are pluggable, you can create custom authentication objects in your application or plugins. If for example you wanted to create an OpenID authentication object. In app/Controller/Component/Auth/
OpenidAuthenticate.php you could put the following:
App::uses('BaseAuthenticate', 'Controller/Component/Auth');class OpenidAuthenticate extends BaseAuthenticate {public function authenticate(CakeRequest $request, CakeResponse $response) {// Do things for OpenID here.
// Return an array of user if they could authenticate the user,// return false if not}}
Authentication objects should return false if they cannot identify the user. And an array of user information if
they can. It’s not required that you extend BaseAuthenticate, only that your authentication object implements anauthenticate() method. The BaseAuthenticate class provides a number of helpful methods that are commonly
used. You can also implement a getUser() method if your authentication object needs to support stateless or cookieless authentication. See the sections on basic and digest authentication below for more information.

Components371

CakePHP Cookbook Documentation, Release 2.x

Using custom authentication objects
Once you’ve created your custom authentication object, you can use them by including them in AuthComponents
authenticate array:$this->Auth->authenticate = array('Openid', // app authentication object.
'AuthBag.Combo', // plugin authentication object.
);

Creating stateless authentication systems
Authentication objects can implement a getUser() method that can be used to support user login systems that
don’t rely on cookies. A typical getUser method looks at the request/environment and uses the information there
to confirm the identity of the user. HTTP Basic authentication for example uses $_SERVER['PHP_AUTH_USER']
and $_SERVER['PHP_AUTH_PW'] for the username and password fields. On each request, these values are used to
re-identify the user and ensure they are valid user. As with authentication object’s authenticate() method thegetUser() method should return an array of user information on success or false on failure.
public function getUser($request) {$username = env('PHP_AUTH_USER');$pass = env('PHP_AUTH_PW');
if (empty($username) || empty($pass)) {return false;}
return $this->_findUser($username, $pass);}
The above is how you could implement getUser method for HTTP basic authentication. The _findUser() method ispart of BaseAuthenticate and identifies a user based on a username and password.
Handling unauthenticated requests
When an unauthenticated user tries to access a protected page first the unauthenticated() method of the last authenticatorin the chain is called. The authenticate object can handle sending response or redirection as appropriate and return
true to indicate no further action is necessary. Due to this the order in which you specify the authenticate object in
AuthComponent::$authenticate property matters.
If authenticator returns null, AuthComponent redirects user to login action. If it’s an AJAX request and AuthComponent::$ajaxLogin is specified that element is rendered else a 403 HTTP status code is returned.
Note: Prior to 2.4 the authenticate objects do not provide an unauthenticated() method.

372

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Displaying auth related flash messages
In order to display the session error messages that Auth generates, you need to add the following code to your layout.
Add the following two lines to the app/View/Layouts/default.ctp file in the body section preferable before thecontent_for_layout line.
// CakePHP 2.7+
echo $this->Flash->render();echo $this->Flash->render('auth');// Prior to 2.7
echo $this->Session->flash();echo $this->Session->flash('auth');You can customize the error messages, and flash settings AuthComponent uses. Using $this->Auth->flash you canconfigure the parameters AuthComponent uses for setting flash messages. The available keys are• element - The element to use, defaults to ‘default’.
• key - The key to use, defaults to ‘auth’• params - The array of additional params to use, defaults to array()
In addition to the flash message settings you can customize other error messages AuthComponent uses. In your controller’s beforeFilter, or component settings you can use authError to customize the error used for when authorization
fails:$this->Auth->authError = "This error shows up with the user tries to access" .
"a part of the website that is protected.";Changed in version 2.4: Sometimes, you want to display the authorization error only after the user has already loggedin. You can suppress this message by setting its value to boolean false
In your controller’s beforeFilter(), or component settings:
if (!$this->Auth->loggedIn()) {$this->Auth->authError = false;}

Hashing passwords
AuthComponent no longer automatically hashes every password it can find. This was removed because it made anumber of common tasks like validation difficult. You should never store plain text passwords, and before saving auser record you should always hash the password.
As of 2.4 the generation and checking of password hashes has been delegated to password hasher classes. Authenticating objects use a new setting passwordHasher which specifies the password hasher class to use. It can be a string
specifying class name or an array with key className stating the class name and any extra keys will be passed topassword hasher constructor as config. The default hasher class Simple can be used for sha1, sha256, md5 hashing.
By default the hash type set in Security class will be used. You can use specific hash type like this:public $components = array('Auth' => array('authenticate' => array('Form' => array((continues on next page)

Components373

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'passwordHasher' => array('className' => 'Simple','hashType' => 'sha256'
)
)
)
)
);
When creating new user records you can hash a password in the beforeSave callback of your model using appropriate
password hasher class:
App::uses('SimplePasswordHasher', 'Controller/Component/Auth');class User extends AppModel {public function beforeSave($options = array()) {if (!empty($this->data[$this->alias]['password'])) {$passwordHasher = new SimplePasswordHasher(array('hashType' => 'sha256'));$this->data[$this->alias]['password'] = $passwordHasher->hash($this->data[$this->alias]['password']);}
return true;}}
You don’t need to hash passwords before calling $this->Auth->login(). The various authentication objects will
hash passwords individually.
Using bcrypt for passwords
In CakePHP 2.3 the BlowfishAuthenticate class was introduced to allow using bcrypt66 a.k.a Blowfish
for hash passwords. Bcrypt hashes are much harder to brute force than passwords stored with sha1. But
BlowfishAuthenticate has been deprecated in 2.4 and instead BlowfishPasswordHasher has been added.
A blowfish password hasher can be used with any authentication class.
passwordHasher setting for the authenticating object:

All you have to do with specify

public $components = array('Auth' => array('authenticate' => array('Form' => array('passwordHasher' => 'Blowfish'))));
66 https://en.wikipedia.org/wiki/Bcrypt

374

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Hashing passwords for digest authentication
Because Digest authentication requires a password hashed in the format defined by the RFC, in order to correctly hash a password for use with Digest authentication you should use the special password hashing function on
DigestAuthenticate. If you are going to be combining digest authentication with any other authentication strategies, it’s also recommended that you store the digest password in a separate column, from the normal password hash:
App::uses('DigestAuthenticate', 'Controller/Component/Auth');class User extends AppModel {public function beforeSave($options = array()) {// make a password for digest auth.
$this->data[$this->alias]['digest_hash'] = DigestAuthenticate::password($this->data[$this->alias]['username'],$this->data[$this->alias]['password'],env('SERVER_NAME'));
return true;}}
Passwords for digest authentication need a bit more information than other password hashes, based on the RFC fordigest authentication.
Note: The third parameter of DigestAuthenticate::password() must match the ‘realm’ config value defined when
DigestAuthentication was configured in AuthComponent::$authenticate. This defaults to env('SERVER_NAME'). You
may wish to use a static string if you want consistent hashes in multiple environments.

Creating custom password hasher classes
Custom password hasher classes need to extend the AbstractPasswordHasher class and need to implement the abstract methods hash() and check(). In app/Controller/Component/Auth/CustomPasswordHasher.php you
could put the following:
App::uses('AbstractPasswordHasher', 'Controller/Component/Auth');class CustomPasswordHasher extends AbstractPasswordHasher {public function hash($password) {// stuff here}
public function check($password, $hashedPassword) {// stuff here}}

Components375

CakePHP Cookbook Documentation, Release 2.x

Manually logging users inSometimes the need arises where you need to manually log a user in, such as just after they registered for your application. You can do this by calling $this->Auth->login() with the user data you want to ‘login’:
public function register() {if ($this->User->save($this->request->data)) {$id = $this->User->id;$this->request->data['User'] = array_merge($this->request->data['User'],array('id' => $id));
unset($this->request->data['User']['password']);$this->Auth->login($this->request->data['User']);return $this->redirect('/users/home');}}Warning: Be sure to manually add the new User id to the array passed to the login method. Otherwise you won’t
have the user id available.

Warning: Be sure to unset password fields before manually passing data into $this->Auth->login(), or it will
get saved in the Session unhashed.

Accessing the logged in user
Once a user is logged in, you will often need some particular information about the current user. You can access thecurrently logged in user using AuthComponent::user(). This method is static, and can be used globally after the
AuthComponent has been loaded. You can access it both as an instance method or as a static method:// Use anywhere
AuthComponent::user('id')// From inside a controller$this->Auth->user('id');

Logging users out
Eventually you’ll want a quick way to de-authenticate someone, and redirect them to where they need to go. Thismethod is also useful if you want to provide a ‘Log me out’ link inside a members’ area of your application:
public function logout() {return $this->redirect($this->Auth->logout());}
Logging out users that logged in with Digest or Basic auth is difficult to accomplish for all clients. Most browsers willretain credentials for the duration they are still open. Some clients can be forced to logout by sending a 401 status code.
Changing the authentication realm is another solution that works for some clients.
376

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Authorization
Authorization is the process of ensuring that an identified/authenticated user is allowed to access the resources they arerequesting. If enabled AuthComponent can automatically check authorization handlers and ensure that logged in users
are allowed to access the resources they are requesting. There are several built-in authorization handlers, and you cancreate custom ones for your application, or as part of a plugin.
• ActionsAuthorize Uses the AclComponent to check for permissions on an action level.
• CrudAuthorize Uses the AclComponent and action -> CRUD mappings to check permissions for resources.
• ControllerAuthorize Calls isAuthorized() on the active controller, and uses the return of that to authorize
a user. This is often the most simple way to authorize users.
Configuring Authorization handlers
You configure authorization handlers using $this->Auth->authorize. You can configure one or many handlers
for authorization. Using multiple handlers allows you to support different ways of checking authorization. Whenauthorization handlers are checked, they will be called in the order they are declared. Handlers should return false, ifthey are unable to check authorization, or the check has failed. Handlers should return true if they were able to checkauthorization successfully. Handlers will be called in sequence until one passes. If all checks fail, the user will be
redirected to the page they came from. Additionally you can halt all authorization by throwing an exception. You willneed to catch any thrown exceptions, and handle them.
You can configure authorization handlers in your controller’s beforeFilter or, in the $components array. You canpass configuration information into each authorization object, using an array:// Basic setup$this->Auth->authorize = array('Controller');// Pass settings in$this->Auth->authorize = array('Actions' => array('actionPath' => 'controllers/'),'Controller');
Much like Auth->authenticate, Auth->authorize, helps you keep your code DRY, by using the all key. This
special key allows you to set settings that are passed to every attached object. The all key is also exposed as
AuthComponent::ALL:// Pass settings in using 'all'$this->Auth->authorize = array(
AuthComponent::ALL => array('actionPath' => 'controllers/'),'Actions','Controller');
In the above example, both the Actions and Controller will get the settings defined for the ‘all’ key. Any settingspassed to a specific authorization object will override the matching key in the ‘all’ key. The core authorize objects
support the following configuration keys.
• actionPath Used by ActionsAuthorize to locate controller action ACO’s in the ACO tree.
• actionMap Action -> CRUD mappings. Used by CrudAuthorize and authorization objects that want to map
actions to CRUD roles.

Components377

CakePHP Cookbook Documentation, Release 2.x• userModel The name of the ARO/Model node user information can be found under. Used with ActionsAuthorize.
Creating Custom Authorize objects
Because authorize objects are pluggable, you can create custom authorize objects in your application or plugins. If forexample you wanted to create an LDAP authorize object. In app/Controller/Component/Auth/LdapAuthorize.
php you could put the following:
App::uses('BaseAuthorize', 'Controller/Component/Auth');class LdapAuthorize extends BaseAuthorize {public function authorize($user, CakeRequest $request) {// Do things for LDAP here.
}}
Authorize objects should return false if the user is denied access, or if the object is unable to perform a check. If theobject is able to verify the user’s access, true should be returned. It’s not required that you extend BaseAuthorize,
only that your authorize object implements an authorize() method. The BaseAuthorize class provides a numberof helpful methods that are commonly used.
Using custom authorize objects
Once you’ve created your custom authorize object, you can use them by including them in your AuthComponent’s
authorize array:$this->Auth->authorize = array('Ldap', // app authorize object.
'AuthBag.Combo', // plugin authorize object.
);

Using no authorization
If you’d like to not use any of the built-in authorization objects, and want to handle things entirely outside of AuthComponent you can set $this->Auth->authorize = false;. By default AuthComponent starts off with authorize= false. If you don’t use an authorization scheme, make sure to check authorization yourself in your controller’s
beforeFilter, or with another component.
Making actions public
There are often times controller actions that you wish to remain entirely public, or that don’t require users to be logged
in. AuthComponent is pessimistic, and defaults to denying access. You can mark actions as public actions by using
AuthComponent::allow(). By marking actions as public, AuthComponent, will not check for a logged in user, norwill authorize objects be checked:// Allow all actions. CakePHP 2.0 (deprecated).
$this->Auth->allow('*');(continues on next page)

378

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// Allow all actions. CakePHP 2.1 and later.
$this->Auth->allow();// Allow only the view and index actions.
$this->Auth->allow('view', 'index');// Allow only the view and index actions.
$this->Auth->allow(array('view', 'index'));

Warning: If you’re using scaffolding, allow all will not identify and allow the scaffolded methods. You have to
specify their action names.
You can provide as many action names as you need to allow(). You can also supply an array containing all the action
names.
Making actions require authorization
By default all actions require authorization. However, after making actions public, you want to revoke the public access.
You can do so using AuthComponent::deny():// remove one action$this->Auth->deny('add');// remove all the actions.
$this->Auth->deny();// remove a group of actions.
$this->Auth->deny('add', 'edit');$this->Auth->deny(array('add', 'edit'));You can provide as many action names as you need to deny(). You can also supply an array containing all the action
names.
Using ControllerAuthorize
ControllerAuthorize allows you to handle authorization checks in a controller callback. This is ideal when you havevery simple authorization, or you need to use a combination of models + components to do your authorization, anddon’t want to create a custom authorize object.
The callback is always called isAuthorized() and it should return a boolean as to whether or not the user is allowed
to access resources in the request. The callback is passed the active user, so it can be checked:
class AppController extends Controller {public $components = array('Auth' => array('authorize' => 'Controller'),);
public function isAuthorized($user = null) {// Any registered user can access public functions
if (empty($this->request->params['admin'])) {(continues on next page)

Components379

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

return true;}// Only admins can access admin functions
if (isset($this->request->params['admin'])) {return (bool)($user['role'] === 'admin');}// Default denyreturn false;}}
The above callback would provide a very simple authorization system where, only users with role = admin could accessactions that were in the admin prefix.
Using ActionsAuthorize
ActionsAuthorize integrates with the AclComponent, and provides a fine grained per action ACL check on each request.
ActionsAuthorize is often paired with DbAcl to give dynamic and flexible permission systems that can be edited byadmin users through the application. It can however, be combined with other Acl implementations such as IniAcl andcustom application Acl backends.
Using CrudAuthorize
CrudAuthorize integrates with AclComponent, and provides the ability to map requests to CRUD operations. Provides the ability to authorize using CRUD mappings. These mapped results are then checked in the AclComponent as
specific permissions.
For example, taking /posts/index as the current request. The default mapping for index, is a read permission
check. The Acl check would then be for the posts controller with the read permission. This allows you to create
permission systems that focus more on what is being done to resources, rather than the specific actions being visited.
Mapping actions when using CrudAuthorize
When using CrudAuthorize or any other authorize objects that use action mappings, it might be necessary to map
additional methods. You can map actions -> CRUD permissions using mapAction(). Calling this on AuthComponent
will delegate to all the of the configured authorize objects, so you can be sure the settings were applied every where:$this->Auth->mapActions(array('create' => array('register'),'view' => array('show', 'display')));
The keys for mapActions should be the CRUD permissions you want to set, while the values should be an array of all
the actions that are mapped to the CRUD permission.

380

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

AuthComponent API
AuthComponent is the primary interface to the built-in authorization and authentication mechanics in CakePHP.
property AuthComponent::$ajaxLoginThe name of an optional view element to render when an AJAX request is made with an invalid or expired
session.
property AuthComponent::$allowedActions
Controller actions for which user validation is not required.
property AuthComponent::$authenticate
Set to an array of Authentication objects you want to use when logging users in. There are several core authentication objects, see the section on Suggested Reading Before Continuing.
property AuthComponent::$authError
Error to display when user attempts to access an object or action to which they do not have access.
Changed in version 2.4: You can suppress authError message from being displayed by setting this value toboolean false.
property AuthComponent::$authorize
Set to an array of Authorization objects you want to use when authorizing users on each request, see the section
on Authorization.
property AuthComponent::$components
Other components utilized by AuthComponent
property AuthComponent::$flash
Settings to use when Auth needs to do a flash message with FlashComponent::set(). Available keys are:• element - The element to use, defaults to ‘default’.
• key - The key to use, defaults to ‘auth’• params - The array of additional params to use, defaults to array()property AuthComponent::$loginAction
A URL (defined as a string or array) to the controller action that handles logins. Defaults to /users/login
property AuthComponent::$loginRedirect
The URL (defined as a string or array) to the controller action users should be redirected to after logging in. Thisvalue will be ignored if the user has an Auth.redirect value in their session.
property AuthComponent::$logoutRedirect
The default action to redirect to after the user is logged out. While AuthComponent does not handle
post-logout redirection, a redirect URL will be returned from AuthComponent::logout(). Defaults to
AuthComponent::$loginAction.
property AuthComponent::$unauthorizedRedirect
Controls handling of unauthorized access. By default unauthorized user is redirected to the referrer URL or
AuthComponent::$loginRedirect or ‘/’. If set to false a ForbiddenException exception is thrown instead of
redirecting.
property AuthComponent::$request
Request object
property AuthComponent::$response
Response object

Components

381

CakePHP Cookbook Documentation, Release 2.x

property AuthComponent::$sessionKey
The session key name where the record of the current user is stored. If unspecified, it will be “Auth.User”.
AuthComponent::allow($action[, $action, ... ])Set one or more actions as public actions, this means that no authorization checks will be performed for the
specified actions. The special value of '*' will mark all the current controllers actions as public. Best used inyour controller’s beforeFilter method.
AuthComponent::constructAuthenticate()
Loads the configured authentication objects.
AuthComponent::constructAuthorize()
Loads the authorization objects configured.
AuthComponent::deny($action[, $action, ... ])Toggle one or more actions previously declared as public actions, as non-public methods. These methods will
now require authorization. Best used inside your controller’s beforeFilter method.
AuthComponent::identify($request, $response)
Parameters• $request (CakeRequest) – The request to use.
• $response (CakeResponse) – The response to use, headers can be sent if authentication
fails.
This method is used by AuthComponent to identify a user based on the information contained in the current
request.
AuthComponent::initialize($Controller)
Initializes AuthComponent for use in the controller.
AuthComponent::isAuthorized($user = null, $request = null)
Uses the configured Authorization adapters to check whether or not a user is authorized. Each adapter will bechecked in sequence, if any of them return true, then the user will be authorized for the request.
AuthComponent::loggedIn()
Returns true if the current client is a logged in user, or false if they are not.
AuthComponent::login($user)
Parameters• $user (array) – Array of logged in user data.
Takes an array of user data to login with. Allows for manual logging of users. Calling user() will populate thesession value with the provided information. If no user is provided, AuthComponent will try to identify a user
using the current request information. See AuthComponent::identify()
AuthComponent::logout()
Returns
A string URL to redirect the logged out user to.
Logs out the current user.
AuthComponent::mapActions($map = array())
Maps action names to CRUD operations. Used for controller-based authentication. Make sure to configure theauthorize property before calling this method. As it delegates $map to all the attached authorize objects.

382

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

static AuthComponent::password($pass)Deprecated since version 2.4.
AuthComponent::redirect($url = null)
Deprecated since version 2.3.
AuthComponent::redirectUrl($url = null)
If no parameter is passed, gets the authentication redirect URL. Pass a URL in to set the destination a user should
be redirected to upon logging in. Will fallback to AuthComponent::$loginRedirect if there is no stored
redirect value.
New in version 2.3.
AuthComponent::shutdown($Controller)
Component shutdown. If user is logged in, wipe out redirect.
AuthComponent::startup($Controller)
Main execution method. Handles redirecting of invalid users, and processing of login form data.
static AuthComponent::user($key = null)Parameters• $key (string) – The user data key you want to fetch. If null, all user data will be returned.
Can also be called as an instance method.
Get data concerning the currently logged in user, you can use a property key to fetch specific data about the user:$id = $this->Auth->user('id');If the current user is not logged in or the key doesn’t exist, null will be returned.
Securityclass SecurityComponent(ComponentCollection $collection, array $settings = array())
The Security Component creates an easy way to integrate tighter security in your application. It provides methods forvarious tasks like:• Restricting which HTTP methods your application accepts.
• CSRF protection.
• Form tampering protection• Requiring that SSL be used.
• Limiting cross controller communication.
Like all components it is configured through several configurable parameters. All of these properties can be set directlyor through setter methods of the same name in your controller’s beforeFilter.
By using the Security Component you automatically get CSRF67 and form tampering protection. Hidden token fields
will automatically be inserted into forms and checked by the Security component. Among other things, a form submission will not be accepted after a certain period of inactivity, which is controlled by the csrfExpires time.
If you are using Security component’s form protection features and other components that process form data in theirstartup() callbacks, be sure to place Security Component before those components in your $components array.
67 https://en.wikipedia.org/wiki/Cross-site_request_forgery

Components383

CakePHP Cookbook Documentation, Release 2.x

Note: When using the Security Component you must use the FormHelper to create your forms. In addition, you must
not override any of the fields’ “name” attributes. The Security Component looks for certain indicators that are createdand managed by the FormHelper (especially those created in create() and end()). Dynamically altering the fields
that are submitted in a POST request (e.g. disabling, deleting or creating new fields via JavaScript) is likely to triggera black-holing of the request. See the $validatePost or $disabledFields configuration parameters.

Handling blackhole callbacks
If an action is restricted by the Security Component it is black-holed as an invalid request which will result in a 400
error by default. You can configure this behavior by setting the $this->Security->blackHoleCallback property
to a callback function in the controller.
SecurityComponent::blackHole(object $controller, string $error)
Black-hole an invalid request with a 400 error or a custom callback. With no callback, the request will be exited.
If a controller callback is set to SecurityComponent::blackHoleCallback, it will be called and passed any error
information.
property SecurityComponent::$blackHoleCallback
A Controller callback that will handle any requests that are blackholed. A blackhole callback can be any public
method on a controller. The callback should expect a parameter indicating the type of error:
public function beforeFilter() {$this->Security->blackHoleCallback = 'blackhole';}
public function blackhole($type) {// handle errors.
}
The $type parameter can have the following values:• ‘auth’ Indicates a form validation error, or a controller/action mismatch error.
• ‘secure’ Indicates an SSL method restriction failure.
Restricting HTTP methods
SecurityComponent::requirePost()
Sets the actions that require a POST request. Takes any number of arguments. Can be called with no arguments
to force all actions to require a POST.
SecurityComponent::requireGet()
Sets the actions that require a GET request. Takes any number of arguments. Can be called with no arguments
to force all actions to require a GET.
SecurityComponent::requirePut()
Sets the actions that require a PUT request. Takes any number of arguments. Can be called with no arguments
to force all actions to require a PUT.
SecurityComponent::requireDelete()
Sets the actions that require a DELETE request. Takes any number of arguments. Can be called with no arguments to force all actions to require a DELETE.

384

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Restrict actions to SSL
SecurityComponent::requireSecure()
Sets the actions that require a SSL-secured request. Takes any number of arguments. Can be called with noarguments to force all actions to require a SSL-secured.
SecurityComponent::requireAuth()
Sets the actions that require a valid Security Component generated token. Takes any number of arguments. Can
be called with no arguments to force all actions to require a valid authentication.
Restricting cross controller communication
property SecurityComponent::$allowedControllers
A list of controllers which can send requests to this controller. This can be used to control cross controller
requests.
property SecurityComponent::$allowedActions
A list of actions which are allowed to send requests to this controller’s actions. This can be used to control crosscontroller requests.
Form tampering prevention
By default the SecurityComponent prevents users from tampering with forms in specific ways.
SecurityComponent will prevent the following things:

The• Unknown fields cannot be added to the form.
• Fields cannot be removed from the form.
• Values in hidden inputs cannot be modified.
Preventing these types of tampering is accomplished by working with the FormHelper and tracking which fields are in
a form. The values for hidden fields are tracked as well. All of this data is combined and turned into a hash. When aform is submitted, the SecurityComponent will use the POST data to build the same structure and compare the hash.
Note: The SecurityComponent will not prevent select options from being added/changed. Nor will it prevent radio
options from being added/changed.
property SecurityComponent::$unlockedFields
Set to a list of form fields to exclude from POST validation. Fields can be unlocked either in the Component, orwith FormHelper::unlockField(). Fields that have been unlocked are not required to be part of the POST
and hidden unlocked fields do not have their values checked.
property SecurityComponent::$validatePost
Set to false to completely skip the validation of POST requests, essentially turning off form validation.

Components385

CakePHP Cookbook Documentation, Release 2.x

CSRF configuration
property SecurityComponent::$csrfCheck
Whether to use CSRF protected forms. Set to false to disable CSRF protection on forms.
property SecurityComponent::$csrfExpires
The duration from when a CSRF token is created that it will expire on. Each form/page request will generate a
new token that can only be submitted once unless it expires. Can be any value compatible with strtotime().
The default is +30 minutes.
property SecurityComponent::$csrfUseOnce
Controls whether or not CSRF tokens are single use. Set to false to not generate new tokens on each request.
One token will be reused until it expires. This reduces the chances of users getting invalid requests because of
token consumption. It has the side effect of making CSRF less secure, as tokens are reusable.
Usage
Using the security component is generally done in the controllers beforeFilter(). You would specify the security
restrictions you want and the Security Component will enforce them on its startup:class WidgetController extends AppController {public $components = array('Security');public function beforeFilter() {$this->Security->requirePost('delete');}}In this example the delete action can only be successfully triggered if it receives a POST request:class WidgetController extends AppController {public $components = array('Security');public function beforeFilter() {if (isset($this->request->params['admin'])) {$this->Security->requireSecure();}}}This example would force all actions that had admin routing to require secure SSL requests:
class WidgetController extends AppController {public $components = array('Security');public function beforeFilter() {if (isset($this->params['admin'])) {$this->Security->blackHoleCallback = 'forceSSL';$this->Security->requireSecure();}(continues on next page)

386

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}
public function forceSSL() {return $this->redirect('https://' . env('SERVER_NAME') . $this->here);}}This example would force all actions that had admin routing to require SSL requests. When the request is black holed, it
will call the nominated forceSSL() callback which will redirect non-secure requests to secure requests automatically.
CSRF protection
CSRF or Cross Site Request Forgery is a common vulnerability in web applications. It allows an attacker to capture
and replay a previous request, and sometimes submit data requests using image tags or resources on other domains.
Double submission and replay attacks are handled by the SecurityComponent CSRF features. They work by adding
a special token to each form request. This token, once used, cannot be used again. If an attempt is made to re-use an
expired token the request will be blackholed.
Using CSRF protection
Simply by adding the SecurityComponent to your components array, you can benefit from the CSRF protection it
provides. By default CSRF tokens are valid for 30 minutes and expire on use. You can control how long tokens last by
setting csrfExpires on the component.
public $components = array('Security' => array('csrfExpires' => '+1 hour'));
You can also set this property in your controller’s beforeFilter:
public function beforeFilter() {$this->Security->csrfExpires = '+1 hour';// ...
}
The csrfExpires property can be any value that is compatible with strtotime()68 . By default the FormHelper will add
a data[_Token][key] containing the CSRF token to every form when the component is enabled.
68 https://www.php.net/manual/en/function.strtotime.php

Components387

CakePHP Cookbook Documentation, Release 2.x

Handling missing or expired tokens
Missing or expired tokens are handled similar to other security violations.
The SecurityComponent
blackHoleCallback will be called with a ‘csrf’ parameter. This helps you filter out CSRF token failures, fromother warnings.
Using per-session tokens instead of one-time use tokens
By default a new CSRF token is generated for each request, and each token can only be used once. If a token is used
twice, the request will be blackholed. Sometimes, this behaviour is not desirable, as it can create issues with single
page applications. You can toggle on longer, multi-use tokens by setting csrfUseOnce to false. This can be done in
the components array, or in the beforeFilter of your controller:
public $components = array('Security' => array('csrfUseOnce' => false));
This will tell the component that you want to re-use a CSRF token until it expires - which is controlled by thecsrfExpires value. If you are having issues with expired tokens, this is a good balance between security and ease ofuse.
Disabling the CSRF protection
There may be cases where you want to disable CSRF protection on your forms for some reason. If you do want to disable
this feature, you can set $this->Security->csrfCheck = false; in your beforeFilter or use the components
array. By default CSRF protection is enabled, and configured to use one-use tokens.
Disabling CSRF and Post Data Validation For Specific Actions
There may be cases where you want to disable all security checks for an action (ex. AJAX requests). You may“unlock” these actions by listing them in $this->Security->unlockedActions in your beforeFilter. The
unlockedActions property will not effect other features of SecurityComponent.
New in version 2.3.
Request Handlingclass RequestHandlerComponent(ComponentCollection $collection, array $settings = array())
The Request Handler component is used in CakePHP to obtain additional information about the HTTP requests thatare made to your applications. You can use it to inform your controllers about AJAX as well as gain additional insight
into content types that the client accepts and automatically changes to the appropriate layout when file extensions areenabled.
By default RequestHandler will automatically detect AJAX requests based on the HTTP-X-Requested-With header thatmany javascript libraries use. When used in conjunction with Router::parseExtensions() RequestHandler willautomatically switch the layout and view files to those that match the requested type. Furthermore, if a helper with thesame name as the requested extension exists, it will be added to the Controllers Helper array. Lastly, if XML/JSON
data is POST’ed to your Controllers, it will be parsed into an array which is assigned to $this->request->data, and388

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

can then be saved as model data. In order to make use of RequestHandler it must be included in your $components
array:
class WidgetController extends AppController {public $components = array('RequestHandler');// Rest of controller}

Obtaining Request Information
Request Handler has several methods that provide information about the client and its request.
RequestHandlerComponent::accepts($type = null)$type can be a string, or an array, or null. If a string, accepts will return true if the client accepts the content
type. If an array is specified, accepts return true if any one of the content types is accepted by the client. If null
returns an array of the content-types that the client accepts. For example:
class PostsController extends AppController {public $components = array('RequestHandler');
public function beforeFilter() {if ($this->RequestHandler->accepts('html')) {// Execute code only if client accepts an HTML (text/html)// response} elseif ($this->RequestHandler->accepts('xml')) {// Execute XML-only code}
if ($this->RequestHandler->accepts(array('xml', 'rss', 'atom'))) {// Executes if the client accepts any of the above: XML, RSS// or Atom}}}
Other request ‘type’ detection methods include:
RequestHandlerComponent::isXml()
Returns true if the current request accepts XML as a response.
RequestHandlerComponent::isRss()
Returns true if the current request accepts RSS as a response.
RequestHandlerComponent::isAtom()Returns true if the current call accepts an Atom response, false otherwise.
RequestHandlerComponent::isMobile()
Returns true if user agent string matches a mobile web browser, or if the client accepts WAP content. The
supported Mobile User Agent strings are:
• Android• AvantGo

Components389

CakePHP Cookbook Documentation, Release 2.x• BlackBerry• DoCoMo• Fennec• iPad• iPhone• iPod• J2ME• MIDP• NetFront• Nokia• Opera Mini
• Opera Mobi
• PalmOS• PalmSource• portalmmm• Plucker• ReqwirelessWeb• SonyEricsson• Symbian• UP.Browser• webOS• Windows CE• Windows Phone OS• XiinoRequestHandlerComponent::isWap()
Returns true if the client accepts WAP content.
All of the above request detection methods can be used in a similar fashion to filter functionality intended for specificcontent types. For example when responding to AJAX requests, you often will want to disable browser caching, andchange the debug level. However, you want to allow caching for non-AJAX requests. The following would accomplishthat:
if ($this->request->is('ajax')) {$this->disableCache();}// Continue Controller action

390

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Obtaining Additional Client Information
RequestHandlerComponent::getAjaxVersion()
Gets Prototype version if call is AJAX, otherwise empty string. The Prototype library sets a special “Prototype
version” HTTP header.
Automatically decoding request data
RequestHandlerComponent::addInputType($type, $handler)
Parameters• $type (string) – The content type alias this attached decoder is for. e.g. ‘json’ or ‘xml’• $handler (array) – The handler information for the type.
Add a request data decoder. The handler should contain a callback, and any additional arguments for the callback.
The callback should return an array of data contained in the request input. For example adding a CSV handler
in your controllers’ beforeFilter could look like:$parser = function ($data) {$rows = str_getcsv($data, "\n");foreach ($rows as &$row) {$row = str_getcsv($row, ',');}
return $rows;};$this->RequestHandler->addInputType('csv', array($parser));
The above example requires PHP 5.3, however you can use any callable69 for the handling function. You can alsopass additional arguments to the callback, this is useful for callbacks like json_decode:$this->RequestHandler->addInputType('json', array('json_decode', true));
The above will make $this->request->data an array of the JSON input data, without the additional true
you’d get a set of stdClass objects.
Responding To Requests
In addition to request detection RequestHandler also provides easy access to altering the output and content type mappings for your application.
RequestHandlerComponent::setContent($name, $type = null)Parameters• $name (string) – The name or file extension of the Content-type ie. html, css, json, xml.
• $type (mixed) – The mime-type(s) that the Content-type maps to.
setContent adds/sets the Content-types for the given name. Allows content-types to be mapped to friendly aliases
and or extensions. This allows RequestHandler to automatically respond to requests of each type in its startup
method. If you are using Router::parseExtension, you should use the file extension as the name of the Contenttype. Furthermore, these content types are used by prefers() and accepts().
69 https://secure.php.net/callback

Components391

CakePHP Cookbook Documentation, Release 2.x

setContent is best used in the beforeFilter() of your controllers, as this will best leverage the automagicness ofcontent-type aliases.
The default mappings are:• javascript text/javascript• js text/javascript• json application/json• css text/css• html text/html, */*• text text/plain• txt text/plain• csv application/vnd.ms-excel, text/plain• form application/x-www-form-urlencoded• file multipart/form-data• xhtml application/xhtml+xml, application/xhtml, text/xhtml• xhtml-mobile application/vnd.wap.xhtml+xml• xml application/xml, text/xml• rss application/rss+xml• atom application/atom+xml• amf application/x-amf• wap text/vnd.wap.wml, text/vnd.wap.wmlscript, image/vnd.wap.wbmp• wml text/vnd.wap.wml• wmlscript text/vnd.wap.wmlscript• wbmp image/vnd.wap.wbmp• pdf application/pdf• zip application/x-zip• tar application/x-tar
RequestHandlerComponent::prefers($type = null)
Determines which content-types the client prefers. If no parameter is given the most likely content type is returned. If $type is an array the first type the client accepts will be returned. Preference is determined primarily
by the file extension parsed by Router if one has been provided, and secondly by the list of content-types in
HTTP_ACCEPT.
RequestHandlerComponent::renderAs($controller, $type)
Parameters• $controller (Controller) – Controller Reference• $type (string) – friendly content type name to render content for ex. xml, rss.
Change the render mode of a controller to the specified type. Will also append the appropriate helper to thecontroller’s helper array if available and not already in the array.

392

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

RequestHandlerComponent::respondAs($type, $options)Parameters• $type (string) – Friendly content type name ex. xml, rss or a full content type likeapplication/x-shockwave• $options (array) – If $type is a friendly type name that has more than one content association, $index is used to select the content type.
Sets the response header based on content-type map names.
RequestHandlerComponent::responseType()
Returns the current response type Content-type header or null if one has yet to be set.
Taking advantage of HTTP cache validation
New in version 2.1.
The HTTP cache validation model is one of the processes used for cache gateways, also known as reverse proxies, todetermine if they can serve a stored copy of a response to the client. Under this model, you mostly save bandwidth, butwhen used correctly you can also save some CPU processing, reducing this way response times.
Enabling the RequestHandlerComponent in your controller automatically activates a check done before rendering theview. This check compares the response object against the original request to determine whether the response was not
modified since the last time the client asked for it.
If response is evaluated as not modified, then the view rendering process is stopped, saving processing time, saving
bandwidth and no content is returned to the client. The response status code is then set to 304 Not Modified.
You can opt-out this automatic checking by setting the checkHttpCache setting to false:
public $components = array('RequestHandler' => array('checkHttpCache' => false));

Using custom ViewClasses
New in version 2.3.
When using JsonView/XmlView you might want to override the default serialization with a custom View class, or add
View classes for other types.
You can map existing and new types to your custom classes.
RequestHandlerComponent::viewClassMap($type, $viewClass)Parameters• $type (string|array) – The type string or map array with format array('json' =>'MyJson')• $viewClass (string) – The viewClass to be used for the type without View appended
You can also set this automatically by using the viewClassMap setting:Components393

CakePHP Cookbook Documentation, Release 2.x

public $components = array('RequestHandler' => array('viewClassMap' => array('json' => 'ApiKit.MyJson','xml' => 'ApiKit.MyXml','csv' => 'ApiKit.Csv')));

Cookieclass CookieComponent(ComponentCollection $collection, array $settings = array())
The CookieComponent is a wrapper around the native PHP setcookie method. It also includes a host of delicious
icing to make coding cookies in your controllers very convenient. Before attempting to use the CookieComponent, youmust make sure that ‘Cookie’ is listed in your controller’s $components array.
Controller Setup
There are a number of controller variables that allow you to configure the way cookies are created and managed.
Defining these special variables in the beforeFilter() method of your controller allows you to define how the CookieComponent works.
Cookie devarifault
able

description

string ‘Cake- The name of the cookie.
$name Cookie’
string null This string is used to encrypt the value written to the cookie. The string should be random and$keydifficult to guess.
When using rijndael or aes encryption, this value must be longer than 32 bytes.
string ‘’
The domain name allowed to access the cookie. For example, use ‘.yourdomain.com’ to allow$doaccess from all your subdomains.
main
int or ‘5
The time when your cookie will expire. Integers are interpreted as seconds. A value of 0 is equivstring Days’ alent to a ‘session cookie’: i.e., the cookie expires when the browser is closed. If a string is set,$time
this will be interpreted with PHP function strtotime(). You can set this directly within the write()
method.
string ‘/’
The server path on which the cookie will be applied. If $path is set to ‘/foo/’, the cookie will only$path
be available within the /foo/ directory and all sub-directories of your domain, such as /foo/bar. The
default value is the entire domain. You can set this directly within the write() method.
boolean false Indicates that the cookie should only be transmitted over a secure HTTPS connection. When set to$setrue, the cookie will only be set if a secure connection exists. You can set this directly within thecurewrite() method.
boolean false Set to true to make HTTP only cookies. Cookies that are HTTP only are not accessible in Javascript.
$httpOnly
The following snippet of controller code shows how to include the CookieComponent and set up the controller variables
needed to write a cookie named ‘baker_id’ for the domain ‘example.com’ which needs a secure connection, is available
on the path ‘/bakers/preferences/’, expires in one hour and is HTTP only:
394

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

public $components = array('Cookie');public function beforeFilter() {parent::beforeFilter();$this->Cookie->name = 'baker_id';$this->Cookie->time = 3600; // or '1 hour'$this->Cookie->path = '/bakers/preferences/';$this->Cookie->domain = 'example.com';$this->Cookie->secure = true; // i.e. only sent if using secure HTTPS$this->Cookie->key = 'qSI232qs*&sXOw!adre@34SAv!@*(XSL#$%)asGb$@11~_+!@#HKis~#^';$this->Cookie->httpOnly = true;$this->Cookie->type('aes');}
Next, let’s look at how to use the different methods of the Cookie Component.
Using the Component
The CookieComponent offers a number of methods for working with Cookies.
CookieComponent::write(mixed $key, mixed $value = null, boolean $encrypt = true, mixed $expires = null)
The write() method is the heart of the cookie component. $key is the cookie variable name you want, and the$value is the information to be stored:$this->Cookie->write('name', 'Larry');
You can also group your variables by using dot notation in the key parameter:$this->Cookie->write('User.name', 'Larry');$this->Cookie->write('User.role', 'Lead');If you want to write more than one value to the cookie at a time, you can pass an array:$this->Cookie->write('User',array('name' => 'Larry', 'role' => 'Lead'));All values in the cookie are encrypted by default. If you want to store the values as plain text, set the thirdparameter of the write() method to false. You should remember to set the encryption mode to ‘aes’ to ensure thatvalues are securely encrypted:$this->Cookie->write('name', 'Larry', false);
The last parameter to write is $expires – the number of seconds until your cookie will expire. For convenience,
this parameter can also be passed as a string that the PHP strtotime() function understands:// Both cookies expire in one hour.
$this->Cookie->write('first_name', 'Larry', false, 3600);$this->Cookie->write('last_name', 'Masters', false, '1 hour');
CookieComponent::read(mixed $key = null)This method is used to read the value of a cookie variable with the name specified by $key.

Components395

CakePHP Cookbook Documentation, Release 2.x// Outputs "Larry"echo $this->Cookie->read('name');// You can also use the dot notation for readecho $this->Cookie->read('User.name');// To get the variables which you had grouped// using the dot notation as an array use the following$this->Cookie->read('User');// this outputs something like array('name' => 'Larry', 'role' => 'Lead')
CookieComponent::check($key)
Parameters• $key (string) – The key to check.
Used to check whether a key/path exists and has a non-null value.
New in version 2.3: CookieComponent::check() was added in 2.3
CookieComponent::delete(mixed $key)
Deletes a cookie variable of the name in $key. Works with dot notation:// Delete a variable$this->Cookie->delete('bar');// Delete the cookie variable bar, but not everything under foo$this->Cookie->delete('foo.bar');CookieComponent::destroy()
Destroys the current cookie.
CookieComponent::type($type)
Allows you to change the encryption scheme. By default the ‘cipher’ scheme is used for backwards compatibility.
However, you should always use either the ‘rijndael’ or ‘aes’ schemes.
Changed in version 2.2: The ‘rijndael’ type was added.
New in version 2.5: The ‘aes’ type was added.
Access Control Lists
class AclComponent(ComponentCollection $collection, array $settings = array())

396

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Understanding How ACL Works
Powerful things require access control. Access control lists are a way to manage application permissions in a finegrained, yet easily maintainable and manageable way.
Access control lists, or ACL, handle two main things: things that want stuff, and things that are wanted. In ACL lingo,
things (most often users) that want to use stuff are represented by access request objects, or AROs. Things in the systemthat are wanted (most often actions or data) are represented by access control objects, or ACOs. The entities are called‘objects’ because sometimes the requesting object isn’t a person. Sometimes you might want to limit the ability ofcertain CakePHP controllers to initiate logic in other parts of your application. ACOs could be anything you want tocontrol, from a controller action, to a web service, to a line in your grandma’s online diary.
To review:• ACO - Access Control Object - Represents something that is wanted• ARO - Access Request Object - Represents something that wants something else
Essentially, ACLs are used to decide when an ARO can have access to an ACO.
In order to help you understand how everything works together, let’s use a semi-practical example. Imagine, for a
moment, a computer system used by a familiar group of adventurers from the fantasy novel Lord of the Rings. The
leader of the group, Gandalf, wants to manage the party’s assets while maintaining a healthy amount of privacy andsecurity for the other members of the party. The first thing he needs to do is create a list of the AROs (requesters)
involved:• Gandalf• Aragorn• Bilbo• Frodo• Gollum• Legolas• Gimli• Pippin• Merry
Note: Realize that ACL is not the same as authentication. ACL is what happens after a user has been authenticated.
Although the two are usually used in concert, it’s important to realize the difference between knowing who someoneis (authentication) and knowing what they can do (ACL).
The next thing Gandalf needs to do is make an initial list of ACOs (resources) the system will handle. His list mightlook something like:• Weapons• The One Ring
• Salted Pork
• Diplomacy• Ale

Components397

CakePHP Cookbook Documentation, Release 2.x

Traditionally, systems were managed using a sort of matrix that showed a basic set of users and permissions relating to
objects. If this information were stored in a table, it might look like this:x
Gandalf
Aragorn
Bilbo
Frodo
GollumLegolasGimli
PippinMerry

Weapons

The Ring

Allow
Allow
Allow
Allow

Salted Pork

Diplomacy

Ale

Allow
Allow

Allow
Allow

Allow
Allow
Allow

Allow
Allow
Allow
Allow

Allow

Allow

Allow
Allow

Allow
Allow

At first glance, it seems that this sort of system could work rather well. Assignments can be made to protect security(only Frodo can access the ring) and protect against accidents (keeping the hobbits out of the salted pork and weapons).
It seems sufficiently fine-grained and easy to read, right?
For a small system like this, maybe a matrix setup would work. But for a growing system, or a system with a large
number of resources (ACOs) and users (AROs), a table can quickly become unwieldy. Imagine trying to control accessto the hundreds of war encampments and trying to manage them by unit. Another drawback to matrices is that you
can’t create logical groups of users or make cascading permissions changes to groups of users based on those logicalgroupings. For example, it would sure be nice to automatically allow the hobbits access to the ale and pork once thebattle is over. Doing it on an individual user basis would be tedious and error prone. Making a cascading permissionschange to all members of the ‘hobbit’ group at once would be easy.
ACL is most usually implemented in a tree structure, with a tree of AROs and a tree of ACOs. By organizing yourobjects in trees, you can deal out permissions in a granular fashion while maintaining a good grip on the big picture.
Being the wise leader he is, Gandalf elects to use ACL in his new system, and organizes his objects along the following
lines:• Fellowship of the Ring™
– Warriors∗ Aragorn∗ Legolas∗ Gimli– Wizards∗ Gandalf– Hobbits∗ Frodo∗ Bilbo∗ Merry∗ Pippin– Visitors∗ Gollum
Using a tree structure for AROs allows Gandalf to define permissions that apply to entire groups of users at once. So,
using our ARO tree, Gandalf can tack on a few group-based permissions:398

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x• Fellowship of the Ring (Deny: all)– Warriors (Allow: Weapons, Ale, Elven Rations, Salted Pork)∗ Aragorn∗ Legolas∗ Gimli– Wizards (Allow: Salted Pork, Diplomacy, Ale)∗ Gandalf– Hobbits (Allow: Ale)
∗ Frodo∗ Bilbo∗ Merry∗ Pippin– Visitors (Allow: Salted Pork)∗ Gollum
If we wanted to use ACL to see whether Pippin was allowed to access the ale, we’d first consult the tree to retrieve hispath through it, which is Fellowship->Hobbits->Pippin. Then we see the different permissions that reside at each of
those points, and use the most specific permission relating to Pippin and the Ale.
ARO Node

Permission Info

ResultFellowship of the Ring
Hobbits
PippinDeny all
Allow ‘ale’–

Denying access to ale.
Allowing access to ale!
Still allowing ale!

Note: Since the ‘Pippin’ node in the ACL tree doesn’t specifically deny access to the ale ACO, the final result is that
we allow access to that ACO.
The tree also allows us to make finer adjustments for more granular control, while still keeping the ability to makesweeping changes to groups of AROs:• Fellowship of the Ring (Deny: all)– Warriors (Allow: Weapons, Ale, Elven Rations, Salted Pork)∗ Aragorn (Allow: Diplomacy)∗ Legolas∗ Gimli– Wizards (Allow: Salted Pork, Diplomacy, Ale)∗ Gandalf– Hobbits (Allow: Ale)
∗ Frodo (Allow: Ring)∗ Bilbo

Components399

CakePHP Cookbook Documentation, Release 2.x∗ Merry (Deny: Ale)∗ Pippin (Allow: Diplomacy)– Visitors (Allow: Salted Pork)∗ Gollum
This approach allows us the ability to make both wide-reaching permissions changes and fine-grained adjustments.
This allows us to say that all hobbits can have access to ale, with one exception: Merry. To see whether Merry canaccess the Ale, we’d find his path in the tree: Fellowship->Hobbits->Merry. Then we’d work our way down, keeping
track of ale-related permissions:
ARO Node

Permission Info

ResultFellowship of the Ring
Hobbits
Merry

Deny all
Allow ‘ale’
Deny Ale

Denying access to ale.
Allowing access to ale!
Denying ale.

Defining Permissions: CakePHP’s INI-based ACL
CakePHP’s first ACL implementation was based on INI files stored in the CakePHP installation. While it’s useful andstable, we recommend that you use the database backed ACL solution, mostly because of its ability to create new ACOs
and AROs on the fly. We meant it for usage in simple applications - and especially for those folks who for some reason
might not be using a database.
By default, CakePHP’s ACL is database-driven. To enable INI-based ACL, you’ll need to tell CakePHP what system
you’re using by updating the following lines in app/Config/core.php:// Change these lines:
Configure::write('Acl.classname', 'DbAcl');Configure::write('Acl.database', 'default');// to look like this:Configure::write('Acl.classname', 'IniAcl');//Configure::write('Acl.database', 'default');
ARO/ACO permissions are specified in /app/Config/acl.ini.php. The basic idea is that AROs are specified in an INI
section that has three properties: groups, allow, and deny.
• groups: names of ARO groups of which this ARO is a member• allow: names of ACOs to which this ARO has access• deny: names of ACOs to which this ARO should be denied access
ACOs are specified in INI sections that only include the allow and deny properties.
As an example, let’s see how the Fellowship ARO structure we’ve been crafting would look in INI syntax:;------------------------------------; AROs;------------------------------------[aragorn]groups = warriors
allow = diplomacy(continues on next page)

400

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[legolas]groups = warriors[gimli]groups = warriors[gandalf]groups = wizards[frodo]groups = hobbits
allow = ring[bilbo]groups = hobbits[merry]groups = hobbits
deny = ale[pippin]groups = hobbits[gollum]groups = visitors;------------------------------------; ARO Groups;------------------------------------[warriors]
allow = weapons, ale, salted_pork[wizards]
allow = salted_pork, diplomacy, ale[hobbits]
allow = ale[visitors]
allow = salted_pork
Now that you’ve got your permissions defined via the INI mechanism, you can skip to the section on checking permissions using the ACL component. Alternatively, you can keep reading to see how you would define the same permissions
using a database ACL.

Components

401

CakePHP Cookbook Documentation, Release 2.x

Defining Permissions: CakePHP’s Database ACL
Now that we’ve covered INI-based ACL permissions, let’s move on to the (more commonly used) database ACL.
Getting Started
The default ACL permissions implementation is powered by a database. CakePHP’s database ACL consists of a set ofcore models and a console application that comes with your CakePHP installation. The models are used by CakePHP
to interact with your database in order to store and retrieve nodes in tree format. The console application is used toinitialize your database and interact with your ACO and ARO trees.
To get started, first you’ll need to make sure your /app/Config/database.php is present and correctly configured.
Once you’ve done that, use the CakePHP console to create your ACL database tables:$ cake schema create DbAcl
Running this command will drop and re-create the tables necessary to store ACO and ARO information in tree format.
The output of the console application should look something like the following:--------------------------------------------------------------Cake Schema Shell--------------------------------------------------------------The following tables will be dropped.
acos
aros
aros_acos
Are you sure you want to drop the tables? (y/n)[n] > yDropping tables.
acos updated.
aros updated.
aros_acos updated.
The following tables will be created.
acos
aros
aros_acos
Are you sure you want to create the tables? (y/n)[y] > yCreating tables.
acos updated.
aros updated.
aros_acos updated.
End create.

Note: This replaces an older deprecated command, “initdb”.
You can also use the SQL file found in app/Config/Schema/db_acl.sql, but that’s nowhere near as fun.

402

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

When finished, you should have three new database tables in your system: acos, aros, and aros_acos (the join table tocreate permissions information between the two trees).
Note: If you’re curious about how CakePHP stores tree information in these tables, read up on modified database tree
traversal. The ACL component uses CakePHP’s Tree to manage the trees’ inheritances. The model class files for ACL
can be found in lib/Cake/Model/.
Now that we’re all set up, let’s work on creating some ARO and ACO trees.
Creating Access Request Objects (AROs) and Access Control Objects (ACOs)
When creating new ACL objects (ACOs and AROs), realize that there are two main ways to name and access nodes.
The first method is to link an ACL object directly to a record in your database by specifying a model name and foreign
key value. The second can be used when an object has no direct relation to a record in your database - you can provide
a textual alias for the object.
Note: In general, when you’re creating a group or higher-level object, use an alias. If you’re managing access to a
specific item or record in the database, use the model/foreign key method.
You create new ACL objects using the core CakePHP ACL models. In doing so, there are a number of fields you’ll
want to use when saving data: model, foreign_key, alias, and parent_id.
The model and foreign_key fields for an ACL object allow you to link the object to its corresponding model record(if there is one). For example, many AROs will have corresponding User records in the database. Setting an ARO’sforeign_key to the User’s ID will allow you to link up ARO and User information with a single User model find()call if you’ve set up the correct model associations. Conversely, if you want to manage edit operation on a specific blog
post or recipe listing, you may choose to link an ACO to that specific model record.
An alias is just a human-readable label you can use to identify an ACL object that has no direct model record correlation. Aliases are generally useful in naming user groups or ACO collections.
The parent_id for an ACL object allows you to fill out the tree structure. Supply the ID of the parent node in the tree
to create a new child.
Before we can create new ACL objects, we’ll need to load up their respective classes. The easiest way to do this is toinclude CakePHP’s ACL Component in your controller’s $components array:public $components = array('Acl');Once we’ve got that done, let’s see some examples of creating these objects. The following code could be placed in acontroller action:
Note: While the examples here focus on ARO creation, the same techniques can be used to create an ACO tree.
Remaining with our Fellowship example, let’s first create our ARO groups. Because they won’t have specific records
tied to them, we’ll use aliases to create the ACL objects. Here, we create them via a controller action, but we could doit elsewhere.
Our approach shouldn’t be drastically new - we’re just using models to save data like we always do:
public function any_action() {$aro = $this->Acl->Aro;(continues on next page)

Components403

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// Here's all of our group info in an array we can iterate through$groups = array(0 => array('alias' => 'warriors'),
1 => array('alias' => 'wizards'),
2 => array('alias' => 'hobbits'),
3 => array('alias' => 'visitors'),);// Iterate and create ARO groups
foreach ($groups as $data) {// Remember to call create() when saving in loops...
$aro->create();// Save data$aro->save($data);}// Other action logic goes here...
}Once we’ve got the groups, we can use the ACL console application to verify the tree structure:$ cake acl view aro
Aro tree:--------------------------------------------------------------[1]warriors[2]wizards[3]hobbits[4]visitors--------------------------------------------------------------The tree is still simple at this point, but at least we’ve got some verification that we’ve got four top-level nodes. Let’s
add some children to those ARO nodes by putting our specific user AROs under these groups. Every good citizenof Middle Earth has an account in our new system, so we’ll tie these ARO records to specific model records in our
database.
Note: When adding child nodes to a tree, make sure to use the ACL node ID, rather than a foreign_key value.

404

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

public function any_action() {$aro = new Aro();// Here are our user records, ready to be linked to new ARO records.
// This data could come from a model and be modified, but we're using static// arrays here for demonstration purposes.
$users = array(0 => array('alias' => 'Aragorn','parent_id' => 1,'model' => 'User','foreign_key' => 2356,),
1 => array('alias' => 'Legolas','parent_id' => 1,'model' => 'User','foreign_key' => 6342,),
2 => array('alias' => 'Gimli','parent_id' => 1,'model' => 'User','foreign_key' => 1564,),
3 => array('alias' => 'Gandalf','parent_id' => 2,'model' => 'User','foreign_key' => 7419,),
4 => array('alias' => 'Frodo','parent_id' => 3,'model' => 'User','foreign_key' => 7451,),
5 => array('alias' => 'Bilbo','parent_id' => 3,'model' => 'User','foreign_key' => 5126,),
6 => array('alias' => 'Merry','parent_id' => 3,'model' => 'User','foreign_key' => 5144,),
7 => array('alias' => 'Pippin','parent_id' => 3,

Components(continues on next page)405

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'model' => 'User','foreign_key' => 1211,),
8 => array('alias' => 'Gollum','parent_id' => 4,'model' => 'User','foreign_key' => 1337,),);// Iterate and create AROs (as children)
foreach ($users as $data) {// Remember to call create() when saving in loops...
$aro->create();//Save data$aro->save($data);}// Other action logic goes here...
}Note: Typically you won’t supply both an alias and a model/foreign_key, but we’re using both here to make thestructure of the tree easier to read for demonstration purposes.
The output of that console application command should now be a little more interesting. Let’s give it a try:
$ cake acl view aro
Aro tree:--------------------------------------------------------------[1]warriors[5]Aragorn[6]Legolas[7]Gimli[2]wizards[8]Gandalf[3]hobbits[9]Frodo[10]Bilbo[11]Merry(continues on next page)

406

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[12]Pippin[4]visitors[13]Gollum--------------------------------------------------------------Now that we’ve got our ARO tree setup properly, let’s discuss a possible approach for structuring an ACO tree. While
we can put together a more abstract representation of our ACO’s, it’s often more practical to model an ACO tree
after CakePHP’s Controller/Action setup. We’ve got five main objects we’re handling in this Fellowship scenario.
The natural setup for this in a CakePHP application consists of a group of models, and ultimately the controllers thatmanipulate them. Beyond the controllers themselves, we’ll want to control access to specific actions in those controllers.
Let’s set up an ACO tree that will mimic a CakePHP app setup. Since we have five ACOs, we’ll create an ACO treethat should end up looking something like the following:• Weapons• Rings• PorkChops• DiplomaticEfforts• Ales
You can create children nodes under each of these five main ACOs, but using CakePHP’s built-in action management
covers basic CRUD operations on a given object. Keeping this in mind will make your ACO trees smaller and easier
to maintain. We’ll see how these are used later on when we discuss how to assign permissions.
Since you’re now a pro at adding AROs, use those same techniques to create this ACO tree. Create these upper levelgroups using the core Aco model.
Assigning Permissions
After creating our ACOs and AROs, we can finally assign permissions between the two groups. This is done using
CakePHP’s core Acl component. Let’s continue with our example.
Here we’ll work with Acl permisions in the context of a controller action. Let’s set up some basic permissions using
the AclComponent in an action inside our controller:class SomethingsController extends AppController {// You might want to place this in the AppController// instead, but here works great too.
public $components = array('Acl');public function index() {// Allow warriors complete access to weapons// Both these examples use the alias syntax$this->Acl->allow('warriors', 'Weapons');// Though the King may not want to let everyone// have unfettered access$this->Acl->deny('warriors/Legolas', 'Weapons', 'delete');Components(continues on next page)407

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$this->Acl->deny('warriors/Gimli','Weapons', 'delete');die(print_r('done', 1));}
The first call we make to the AclComponent allows any user under the ‘warriors’ ARO group full access to anything
under the ‘Weapons’ ACO group. Here we’re just addressing ACOs and AROs by their aliases.
Notice the usage of the third parameter? One nice thing about the CakePHP ACL setup is that permissions contain fourbuilt-in properties related to CRUD (create, read, update, and delete) actions for convenience. The default options forthat parameter are create, read, update, and delete but you can add a column in the aros_acos database table(prefixed with _ - for example _admin) and use it alongside the defaults.
The second set of calls is an attempt to make a more fine-grained permission decision. We want Aragorn to keep his
full-access privileges, but we want to deny other warriors in the group the ability to delete Weapons records. We’reusing the alias syntax to address the AROs above, but you might want to use the model/foreign key syntax yourself.
What we have above is equivalent to this:// 6342 = Legolas// 1564 = Gimli$this->Acl->deny(array('model' => 'User', 'foreign_key' => 6342),'Weapons','delete');$this->Acl->deny(array('model' => 'User', 'foreign_key' => 1564),'Weapons','delete');Note: Addressing a node using the alias syntax uses a slash-delimited string (‘/users/employees/developers’). Addressing a node using model/foreign key syntax uses an array with two parameters: array('model' => 'User','foreign_key' => 8282).
The next section will help us validate our setup by using the AclComponent to check the permissions we’ve just set up.
Checking Permissions: The ACL Component
Let’s use the AclComponent to make sure dwarves and elves can’t remove things from the armory. At this point, weshould be able to use the AclComponent to make a check between the ACOs and AROs we’ve created. The basic syntax
for making a permissions check is:$this->Acl->check($aro, $aco, $action = '*');
Let’s give it a try inside a controller action:
public function index() {// These all return true:$this->Acl->check('warriors/Aragorn', 'Weapons');(continues on next page)

408

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$this->Acl->check('warriors/Aragorn', 'Weapons', 'create');$this->Acl->check('warriors/Aragorn', 'Weapons', 'read');$this->Acl->check('warriors/Aragorn', 'Weapons', 'update');$this->Acl->check('warriors/Aragorn', 'Weapons', 'delete');// Remember, we can use the model/id syntax// for our user AROs
$this->Acl->check(array('User' => array('id' => 2356)), 'Weapons');// These also return true:$result = $this->Acl->check('warriors/Legolas', 'Weapons', 'create');$result = $this->Acl->check('warriors/Gimli', 'Weapons', 'read');// But these return false:$result = $this->Acl->check('warriors/Legolas', 'Weapons', 'delete');$result = $this->Acl->check('warriors/Gimli', 'Weapons', 'delete');}The usage here is for demonstration, but this type of checking can be used to decide whether to allow an action, show
an error message, or redirect the user to a login.

Helpers
CakePHP features a number of helpers that aid in view creation. They assist in creating well-formed markup (including
forms), aid in formatting text, times and numbers, and can even integrate with popular JavaScript libraries. Here is asummary of the built-in helpers.
Read Helpers to learn more about helpers, their API, and how you can create and use your own helpers.

Helpers
CakePHP features a number of helpers that aid in view creation. They assist in creating well-formed markup (including
forms), aid in formatting text, times and numbers, and can even integrate with popular JavaScript libraries. Here is asummary of the built-in helpers.
Read Helpers to learn more about helpers, their API, and how you can create and use your own helpers.

Utilities
Beyond the core MVC components, CakePHP includes a great selection of utility classes that help you do everythingfrom webservice requests, to caching, to logging, internationalization and more.

Helpers409

CakePHP Cookbook Documentation, Release 2.x

Utilities
CachingCaching is frequently used to reduce the time it takes to create or read from other resources. Caching is often used tomake reading from expensive resources less expensive. You can easily store the results of expensive queries, or remote
webservice access that doesn’t frequently change in a cache. Once in the cache, re-reading the stored resource from thecache is much cheaper than accessing the remote resource.
Caching in CakePHP is primarily facilitated by the Cache class. This class provides a set of static methods that provide
a uniform API to dealing with all different types of Caching implementations. CakePHP comes with several cacheengines built-in, and provides an easy system to implement your own caching systems. The built-in caching enginesare:• FileCache File cache is a simple cache that uses local files. It is the slowest cache engine, and doesn’t provide
as many features for atomic operations. However, since disk storage is often quite cheap, storing large objects,
or elements that are infrequently written work well in files. This is the default Cache engine for 2.3+
• ApcCache APC cache uses the APC70 or APCu71 extension. These extensions use shared memory on the webserver to store objects. This makes it very fast, and able to provide atomic read/write features. By default
CakePHP in 2.0-2.2 will use this cache engine, if it’s available.
• Wincache Wincache uses the Wincache72 extension. Wincache is similar to APC in features and performance,but optimized for Windows and Microsoft IIS.
• XcacheEngine Xcache73 is a PHP extension that provides similar features to APC.
• MemcacheEngine Uses the Memcache74 extension. Memcache provides a very fast cache system that can be
distributed across many servers, and provides atomic operations.
• MemcachedEngine Uses the Memcached75 extension. It also interfaces with memcache but provides better
performance.
• RedisEngine Uses the phpredis76 extension (2.2.3 minimum). Redis provides a fast and persistent cache system
similar to memcached, also provides atomic operations.
Changed in version 2.3: FileEngine is always the default cache engine. In the past a number of people had difficultysetting up and deploying APC correctly both in CLI + web. Using files should make setting up CakePHP simpler for
new developers.
Changed in version 2.5: The Memcached engine was added. And the Memcache engine was deprecated.
Regardless of the CacheEngine you choose to use, your application interacts with Cache in a consistent manner. Thismeans you can easily swap cache engines as your application grows. In addition to the Cache class, the CacheHelper
allows for full page caching, which can greatly improve performance as well.
70 https://www.php.net/manual/book.apcu
71 https://www.php.net/apcu

72 https://www.php.net/wincache

73 https://en.wikipedia.org/wiki/List_of_PHP_accelerators#XCache
74 https://www.php.net/memcache

75 https://www.php.net/memcached

76 https://github.com/phpredis/phpredis

410

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Configuring Cache classConfiguring the Cache class can be done anywhere, but generally you will want to configure Cache in app/Config/
bootstrap.php. You can configure as many cache configurations as you need, and use any mixture of cache engines.
CakePHP uses two cache configurations internally, which are configured in app/Config/core.php. If you are using
APC or Memcache you should make sure to set unique keys for the core caches. This will prevent multiple applications
from overwriting each other’s cached data.
Using multiple cache configurations can help reduce the number of times you need to use Cache::set() as wellas centralize all your cache settings. Using multiple configurations also lets you incrementally change the storage asneeded.
Note: You must specify which engine to use. It does not default to File.
Example:// Cache configuration for data that can be cached for a short time only.
Cache::config('short', array('engine' => 'File','duration' => '+1 hours','path' => CACHE,'prefix' => 'cake_short_'));// Cache configuration for data that can be cached for a long time.
Cache::config('long', array('engine' => 'File','duration' => '+1 week','probability' => 100,
'path' => CACHE . 'long' . DS,));
By placing the above code in your app/Config/bootstrap.php you will have two additional Cache configurations. The name of these configurations ‘short’ or ‘long’ is used as the $config parameter for Cache::write()and Cache::read(), e.g. Cache::read('my_data', 'short').
Note: When using the FileEngine you might need to use the mask option to ensure cache files are made with thecorrect permissions.
New in version 2.4: In debug mode missing directories will now be automatically created to avoid unnecessary errors
thrown when using the FileEngine.

Utilities

411

CakePHP Cookbook Documentation, Release 2.x

Creating a storage engine for Cache
You can provide custom Cache adapters in app/Lib as well as in plugins using $plugin/Lib. App/plugin cacheengines can also override the core engines. Cache adapters must be in a cache directory. If you had a cache engine
named MyCustomCacheEngine it would be placed in either app/Lib/Cache/Engine/MyCustomCacheEngine.php
as an app/libs or in $plugin/Lib/Cache/Engine/MyCustomCacheEngine.php as part of a plugin. Cache configs
from plugins need to use the plugin dot syntax.
Cache::config('custom', array('engine' => 'CachePack.MyCustomCache',// ...
));

Note: App and Plugin cache engines should be configured in app/Config/bootstrap.php. If you try to configure
them in core.php they will not work correctly.
Custom Cache engines must extend CacheEngine which defines a number of abstract methods as well as provides a
few initialization methods.
The required API for a CacheEngine isclass CacheEngine
The base class for all cache engines used with Cache.
CacheEngine::write($key, $value, $config = 'default')
Returnsboolean for success.
Write value for a key into cache, optional string $config specifies configuration name to write to.
CacheEngine::read($key, $config = 'default')
Returns
The cached value or false for failure.
Read a key from the cache, optional string $config specifies configuration name to read from. Return false toindicate the entry has expired or does not exist.
CacheEngine::delete($key, $config = 'default')
Returns
Boolean true on success.
Delete a key from the cache, optional string $config specifies configuration name to delete from. Return false toindicate that the entry did not exist or could not be deleted.
CacheEngine::clear($check)
Returns
Boolean true on success.
Delete all keys from the cache. If $check is true, you should validate that each value is actually expired.
CacheEngine::clearGroup($group)
Returns
Boolean true on success.

412

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Delete all keys from the cache belonging to the same group.
CacheEngine::decrement($key, $offset = 1)
Returns
The decremented value on success, false otherwise.
Decrement a number under the key and return decremented value
CacheEngine::increment($key, $offset = 1)
Returns
The incremented value on success, false otherwise.
Increment a number under the key and return incremented value
CacheEngine::gc()
Not required, but used to do clean up when resources expire. FileEngine uses this to delete files containing
expired content.
CacheEngine::add($key, $value)
Set a value in the cache if it did not already exist. Should use an atomic check and set where possible.
New in version 2.8: add method was added in 2.8.0.
Using Cache to store common query results
You can greatly improve the performance of your application by putting results that infrequently change, or that are
subject to heavy reads into the cache. A perfect example of this are the results from Model::find(). A method thatuses Cache to store results could look like:class Post extends AppModel {public function newest() {$result = Cache::read('newest_posts', 'long');if (!$result) {$result = $this->find('all', array('order' => 'Post.updated DESC', 'limit' =>˓→ 10));
Cache::write('newest_posts', $result, 'long');}
return $result;}}
You could improve the above code by moving the cache reading logic into a behavior, that read from the cache, or ran
the associated model method. That is an exercise you can do though.
As of 2.5 you can accomplish the above much more simple by using Cache::remember(). Assuming you are using
PHP 5.3 or newer, using the remember() method would look like:class Post extends AppModel {public function newest() {$model = $this;return Cache::remember('newest_posts', function () use ($model){return $model->find('all', array('order' => 'Post.updated DESC',(continues on next page)

Utilities

413

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'limit' => 10));}, 'long');}}

Using Cache to store counters
Counters for various things are easily stored in a cache. For example, a simple countdown for remaining ‘slots’ in acontest could be stored in Cache. The Cache class exposes atomic ways to increment/decrement counter values in aneasy way. Atomic operations are important for these values as it reduces the risk of contention, a scenario where twousers simultaneously lower the value by one, resulting in an incorrect value.
After setting an integer value, you can manipulate it using Cache::increment() and Cache::decrement():
Cache::write('initial_count', 10);// Later on
Cache::decrement('initial_count');// or
Cache::increment('initial_count');

Note: Incrementing and decrementing do not work with FileEngine. You should use APC, Redis or Memcachedinstead.

Using groups
New in version 2.2.
Sometimes you will want to mark multiple cache entries to belong to a certain group or namespace. This is a common
requirement for mass-invalidating keys whenever some information changes that is shared among all entries in the samegroup. This is possible by declaring the groups in cache configuration:
Cache::config('site_home', array('engine' => 'Redis','duration' => '+999 days','groups' => array('comment', 'post')));
Let’s say you want to store the HTML generated for your homepage in cache, but would also want to automaticallyinvalidate this cache every time a comment or post is added to your database. By adding the groups comment and post,
we have effectively tagged any key stored into this cache configuration with both group names.
For instance, whenever a new post is added, we could tell the Cache engine to remove all entries associated to the postgroup:// Model/Post.php
public function afterSave($created, $options = array()) {(continues on next page)

414

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)if ($created) {Cache::clearGroup('post', 'site_home');}}New in version 2.4.
Cache::groupConfigs() can be used to retrieve mapping between group and configurations, i.e.: having the samegroup:// Model/Post.php/*** A variation of previous example that clears all Cache configurations* having the same group*/public function afterSave($created, $options = array()) {if ($created) {$configs = Cache::groupConfigs('post');foreach ($configs['post'] as $config) {Cache::clearGroup('post', $config);}}}
Groups are shared across all cache configs using the same engine and same prefix. If you are using groups and want totake advantage of group deletion, choose a common prefix for all your configs.
Cache API
class Cache
The Cache class in CakePHP provides a generic frontend for several backend caching systems. Different Cache
configurations and engines can be set up in your app/Config/core.php
static Cache::config($name = null, $settings = array())
Cache::config() is used to create additional Cache configurations. These additional configurations can havedifferent duration, engines, paths, or prefixes than your default cache config.
static Cache::read($key, $config = 'default')
Cache::read() is used to read the cached value stored under $key from the $config. If $config is null thedefault config will be used. Cache::read() will return the cached value if it is a valid cache or false if thecache has expired or doesn’t exist. The contents of the cache might evaluate false, so make sure you use the strict
comparison operators: === or !==.
For example:$cloud = Cache::read('cloud');if ($cloud !== false) {return $cloud;}// generate cloud data(continues on next page)

Utilities

415

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// ...
// store data in cache
Cache::write('cloud', $cloud);return $cloud;static Cache::write($key, $value, $config = 'default')
Cache::write() will write a $value to the Cache. You can read or delete this value later by referring to it by$key. You may specify an optional configuration to store the cache in as well. If no $config is specified, default
will be used. Cache::write() can store any type of object and is ideal for storing results of model finds:
if (($posts = Cache::read('posts')) === false) {$posts = $this->Post->find('all');Cache::write('posts', $posts);}
Using Cache::write() and Cache::read() to easily reduce the number of trips made to the database to fetch
posts.
static Cache::delete($key, $config = 'default')
Cache::delete() will allow you to completely remove a cached object from the Cache store.
static Cache::set($settings = array(), $value = null, $config = 'default')
Cache::set() allows you to temporarily override a cache config’s settings for one operation (usually a read or
write). If you use Cache::set() to change the settings for a write, you should also use Cache::set() before
reading the data back in. If you fail to do so, the default settings will be used when the cache key is read.
Cache::set(array('duration' => '+30 days'));Cache::write('results', $data);// Later on
Cache::set(array('duration' => '+30 days'));$results = Cache::read('results');
If you find yourself repeatedly calling Cache::set() then perhaps you should create a new Cache::config().
This will remove the need to call Cache::set().
static Cache::increment($key, $offset = 1, $config = 'default')
Atomically increment a value stored in the cache engine. Ideal for modifying counters or semaphore type values.
static Cache::decrement($key, $offset = 1, $config = 'default')
Atomically decrement a value stored in the cache engine. Ideal for modifying counters or semaphore type values.
static Cache::add($key, $value, $config = 'default')
Add data to the cache, but only if the key does not exist already. In the case that data did exist, this method will
return false. Where possible data is checked & set atomically.
New in version 2.8: add method was added in 2.8.0.
static Cache::clear($check, $config = 'default')
Destroy all cached values for a cache configuration. In engines like Apc, Memcache and Wincache, the cacheconfiguration’s prefix is used to remove cache entries. Make sure that different cache configurations have different
prefixes.

416

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Cache::clearGroup($group, $config = 'default')
Returns
Boolean true on success.
Delete all keys from the cache belonging to the same group.
static Cache::gc($config)
Garbage collects entries in the cache configuration. This is primarily used by FileEngine. It should be implemented by any Cache engine that requires manual eviction of cached data.
static Cache::groupConfigs($group = null)
Returns
Array of groups and its related configuration names.
Retrieve group names to config mapping.
static Cache::remember($key, $callable, $config = 'default')
Provides an easy way to do read-through caching. If the cache key exists it will be returned. If the key does notexist, the callable will be invoked and the results stored in the cache at the provided key.
For example, you often want to cache query results. You could use remember() to make this simple. Assuming
you are using PHP 5.3 or newer:class Articles extends AppModel {function all() {$model = $this;return Cache::remember('all_articles', function () use ($model){return $model->find('all');});}}
New in version 2.5: remember() was added in 2.5.
CakeEmailclass CakeEmail(mixed $config = null)
CakeEmail is a new class to send email. With this class you can send email from any place in your application. In
addition to using the EmailComponent from your controller, you can also send mail from Shells and Models.
This class replaces the EmailComponent and gives more flexibility in sending emails. For example, you can create
your own transports to send email instead of using the provided SMTP and Mail transports.
Basic usage
First of all, you should ensure the class is loaded using App::uses():
App::uses('CakeEmail', 'Network/Email');Using CakeEmail is similar to using EmailComponent. But instead of using attributes you use methods. Example:

Utilities

417

CakePHP Cookbook Documentation, Release 2.x$Email = new CakeEmail();$Email->from(array('me@example.com' => 'My Site'));$Email->to('you@example.com');$Email->subject('About');$Email->send('My message');
To simplify things, all of the setter methods return the instance of class. You can re-write the above code as:$Email = new CakeEmail();$Email->from(array('me@example.com' => 'My Site'))->to('you@example.com')->subject('About')->send('My message');

Choosing the sender
When sending email on behalf of other people it’s often a good idea to define the original sender using the Sender
header. You can do so using sender():$Email = new CakeEmail();$Email->sender('app@example.com', 'MyApp emailer');
Note: It’s also a good idea to set the envelope sender when sending mail on another person’s behalf. This prevents
them from getting any messages about deliverability.

Configuration
Similar to database configuration, email configuration can be centralized in a class.
Create the file app/Config/email.php with the class EmailConfig. The app/Config/email.php.default has
an example of this file.
CakeEmail will create an instance of the EmailConfig class to access the config. If you have dynamic data to put inthe configs, you can use the constructor to do that:class EmailConfig {public function __construct() {// Do conditional assignments here.
}}
It is not required to create app/Config/email.php, CakeEmail can be used without it and use respective methods
to set all configurations separately or load an array of configs.
To load a config from EmailConfig you can use the config() method or pass it to the constructor of CakeEmail:$Email = new CakeEmail();$Email->config('default');//or in constructor::(continues on next page)

418

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$Email = new CakeEmail('default');// Implicit 'default' config used as of 2.7$Email = new CakeEmail();
Instead of passing a string which matches the configuration name in EmailConfig you can also just load an array ofconfigs:$Email = new CakeEmail();$Email->config(array('from' => 'me@example.org', 'transport' => 'MyCustom'));//or in constructor::$Email = new CakeEmail(array('from' => 'me@example.org', 'transport' => 'MyCustom'));Note: Use $Email->config() or the constructor to set the log level to log email headers and message. Using$Email->config(array('log' => true)); will use LOG_DEBUG. See also CakeLog::write()
You can configure SSL SMTP servers such as Gmail. To do so, prefix the host with 'ssl://' and configure the port
value accordingly. Example:
class EmailConfig {public $gmail = array('host' => 'ssl://smtp.gmail.com','port' => 465,
'username' => 'my@gmail.com','password' => 'secret','transport' => 'Smtp');}
You can also use tls:// to prefer TLS for connection level encryption.
Warning: You will need to have access for less secure apps enabled in your Google account for this to work:
Allowing less secure apps to access your account77 .

Note: To use either the ssl:// or tls:// feature, you will need to have the SSL configured in your PHP install.
As of 2.3.0 you can also enable STARTTLS SMTP extension using the tls option:
class EmailConfig {public $gmail = array('host' => 'smtp.gmail.com','port' => 465,
'username' => 'my@gmail.com','password' => 'secret','transport' => 'Smtp','tls' => true(continues on next page)
77 https://support.google.com/accounts/answer/6010255

Utilities419

CakePHP Cookbook Documentation, Release 2.x(continued from previous page));}The above configuration would enable STARTTLS communication for email messages.
New in version 2.3: Support for TLS delivery was added in 2.3
Configurations
The following configuration keys are used:• 'from': Email or array of sender. See CakeEmail::from().
• 'sender': Email or array of real sender. See CakeEmail::sender().
• 'to': Email or array of destination. See CakeEmail::to().
• 'cc': Email or array of carbon copy. See CakeEmail::cc().
• 'bcc': Email or array of blind carbon copy. See CakeEmail::bcc().
• 'replyTo': Email or array to reply the e-mail. See CakeEmail::replyTo().
• 'readReceipt': Email address or an array of addresses to receive the receipt of read.
CakeEmail::readReceipt().

See• 'returnPath': Email address or and array of addresses to return if have some error.
CakeEmail::returnPath().

See• 'messageId': Message ID of e-mail. See CakeEmail::messageId().
• 'subject': Subject of the message. See CakeEmail::subject().
• 'message': Content of message. Do not set this field if you are using rendered content.
• 'headers': Headers to be included. See CakeEmail::setHeaders().
• 'viewRender': If you are using rendered content, set the view class name. See CakeEmail::viewRender().
• 'template': If you are using rendered content, set the template name. See CakeEmail::template().
• 'theme': Theme used when rendering template. See CakeEmail::theme().
• 'layout': If you are using rendered content, set the layout to render. If you want to render a template without
layout, set this field to null. See CakeEmail::template().
• 'viewVars': If you are using rendered content, set the array with variables to be used in the view. See
CakeEmail::viewVars().
• 'attachments': List of files to attach. See CakeEmail::attachments().
• 'emailFormat': Format of email (html, text or both). See CakeEmail::emailFormat().
• 'transport': Transport name. See CakeEmail::transport().
• 'helpers': Array of helpers used in the email template.
All of these configurations are optional, except 'from'. If you put more configurations in this array, the configurations
will be used in the CakeEmail::config() method and passed to the transport class config(). For example, if you
are using the SMTP transport, you should pass the host, port and other configurations.

420

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Note: The values of above keys using Email or array, like from, to, cc, etc will be passed as first parameter of corresponding methods. The equivalent for: CakeEmail::from('my@example.com', 'My Site') would be defined as'from' => array('my@example.com' => 'My Site') in your config.

Setting headers
In CakeEmail you are free to set whatever headers you want. When migrating to use CakeEmail, do not forget to put
the X- prefix in your headers.
See CakeEmail::setHeaders() and CakeEmail::addHeaders()
Sending templated emails
Emails are often much more than just a simple text message. In order to facilitate that, CakePHP provides a way tosend emails using CakePHP’s view layer.
The templates for emails reside in a special folder in your applications View directory called Emails. Email views canalso use layouts, and elements just like normal views:$Email = new CakeEmail();$Email->template('welcome', 'fancy')->emailFormat('html')->to('bob@example.com')->from('app@domain.com')->send();The above would use app/View/Emails/html/welcome.ctp for the view, and app/View/Layouts/Emails/
html/fancy.ctp for the layout. You can send multipart templated email messages as well:$Email = new CakeEmail();$Email->template('welcome', 'fancy')->emailFormat('both')->to('bob@example.com')->from('app@domain.com')->send();This would use the following view files:• app/View/Emails/text/welcome.ctp• app/View/Layouts/Emails/text/fancy.ctp• app/View/Emails/html/welcome.ctp• app/View/Layouts/Emails/html/fancy.ctp
When sending templated emails you have the option of sending either text, html or both.
You can set view variables with CakeEmail::viewVars():$Email = new CakeEmail('templated');$Email->viewVars(array('value' => 12345));
In your email templates you can use these with:

Utilities421

CakePHP Cookbook Documentation, Release 2.x<p>Here is your value: <b><?php echo $value; ?></b></p>
You can use helpers in emails as well, much like you can in normal view files. By default only the HtmlHelper isloaded. You can load additional helpers using the helpers() method:$Email->helpers(array('Html', 'Custom', 'Text'));When setting helpers be sure to include ‘Html’ or it will be removed from the helpers loaded in your email template.
If you want to send email using templates in a plugin you can use the familiar plugin syntax to do so:$Email = new CakeEmail();$Email->template('Blog.new_comment', 'Blog.auto_message');
The above would use templates from the Blog plugin as an example.
In some cases, you might need to override the default template provided by plugins. You can do this using themes bytelling CakeEmail to use appropriate theme using CakeEmail::theme() method:$Email = new CakeEmail();$Email->template('Blog.new_comment', 'Blog.auto_message');$Email->theme('TestTheme');This allows you to override the new_comment template in your theme without modifying the Blog plugin. The template
file needs to be created in the following path: APP/View/Themed/TestTheme/Blog/Emails/text/new_comment.
ctp.
Sending attachments
CakeEmail::attachments($attachments = null)
You can attach files to email messages as well. There are a few different formats depending on what kind of files you
have, and how you want the filenames to appear in the recipient’s mail client:
1. String: $Email->attachments('/full/file/path/file.png') will attach this file with the name file.png.
2. Array: $Email->attachments(array('/full/file/path/file.png')) will have the same behavior as
using a string.
3. Array with key: $Email->attachments(array('photo.png' => '/full/some_hash.png')) will attach
some_hash.png with the name photo.png. The recipient will see photo.png, not some_hash.png.
4. Nested arrays:$Email->attachments(array('photo.png' => array('file' => '/full/some_hash.png','mimetype' => 'image/png','contentId' => 'my-unique-id')));
The above will attach the file with different mimetype and with custom Content ID (when set the content ID theattachment is transformed to inline). The mimetype and contentId are optional in this form.
4.1.
When you are using the contentId, you can use the file in the HTML body like <img
src="cid:my-content-id">.
422

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

4.2. You can use the contentDisposition option to disable the Content-Disposition header for an attachment. This is useful when sending ical invites to clients using outlook.
4.3 Instead of the file option you can provide the file contents as a string using the data option. This allows
you to attach files without needing file paths to them.
Changed in version 2.3: The contentDisposition option was added.
Changed in version 2.4: The data option was added.
Using transports
Transports are classes designed to send the e-mail over some protocol or method. CakePHP supports the Mail (default),
Debug and SMTP transports.
To configure your method, you must use the CakeEmail::transport() method or have the transport in your configuration.
Creating custom Transports
You are able to create your custom transports to integrate with others email systems (like SwiftMailer). To create yourtransport, first create the file app/Lib/Network/Email/ExampleTransport.php (where Example is the name ofyour transport). To start off your file should look like:
App::uses('AbstractTransport', 'Network/Email');class ExampleTransport extends AbstractTransport {public function send(CakeEmail $Email) {// magic inside!
}}
You must implement the method send(CakeEmail $Email) with your custom logic. Optionally, you can implement
the config($config) method. config() is called before send() and allows you to accept user configurations. By
default, this method puts the configuration in protected attribute $_config.
If you need to call additional methods on the transport before send, you can use CakeEmail::transportClass() toget an instance of the transport. Example:$yourInstance = $Email->transport('your')->transportClass();$yourInstance->myCustomMethod();$Email->send();Utilities423

CakePHP Cookbook Documentation, Release 2.x

Relaxing address validation rules
CakeEmail::emailPattern($pattern = null)If you are having validation issues when sending to non-compliant addresses, you can relax the pattern used to validate
email addresses. This is sometimes necessary when dealing with some Japanese ISP’s:$email = new CakeEmail('default');// Relax the email pattern, so you can send// to non-conformant addresses.
$email->emailPattern($newPattern);
New in version 2.4.
Sending messages quickly
Sometimes you need a quick way to fire off an email, and you don’t necessarily want do setup a bunch of configurationahead of time. CakeEmail::deliver() is intended for that purpose.
You can create your configuration in EmailConfig, or use an array with all options that you need and use the static
method CakeEmail::deliver(). Example:
CakeEmail::deliver('you@example.com', 'Subject', 'Message', array('from' => 'me@example.
˓→com'));
This method will send an email to you@example.com, from me@example.com with subject Subject and content Message.
The return of deliver() is a CakeEmail instance with all configurations set. If you do not want to send the emailright away, and wish to configure a few things before sending, you can pass the 5th parameter as false.
The 3rd parameter is the content of message or an array with variables (when using rendered content).
The 4th parameter can be an array with the configurations or a string with the name of configuration in EmailConfig.
If you want, you can pass the to, subject and message as null and do all configurations in the 4th parameter (as array orusing EmailConfig). Check the list of configurations to see all accepted configs.
Sending emails from CLI
Changed in version 2.2: The domain() method was added in 2.2
When sending emails within a CLI script (Shells, Tasks, . . . ) you should manually set the domain name for CakeEmailto use. The domain name is used as the host name for the message id (since there is no host name in a CLI environment):$Email->domain('www.example.org');// Results in message ids like ``<UUID@www.example.org>`` (valid)// instead of `<UUID@>`` (invalid)
A valid message id can help to prevent emails ending up in spam folders. If you are generating links in your email
bodies you will also need to set the App.fullBaseUrl configure value.

424

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Folder & File
The Folder and File utilities are convenience classes to help you read from and write/append to files; list files within afolder and other common directory related tasks.
Basic usage
Ensure the classes are loaded using App::uses():<?php
App::uses('Folder', 'Utility');
App::uses('File', 'Utility');
Then we can setup a new folder instance:<?php$dir = new Folder('/path/to/folder');and search for all .ctp files within that folder using regex:<?php$files = $dir->find('.*\.ctp');
Now we can loop through the files and read from or write/append to the contents or simply delete the file:<?php
foreach ($files as $file) {$file = new File($dir->pwd() . DS . $file);$contents = $file->read();// $file->write('I am overwriting the contents of this file');// $file->append('I am adding to the bottom of this file.');// $file->delete(); // I am deleting this file
$file->close(); // Be sure to close the file when you're done}

Folder API
class Folder(string $path = false, boolean $create = false, string|boolean $mode = false)<?php// Create a new folder with 0755 permissions$dir = new Folder('/path/to/folder', true, 0755);
property Folder::$path
Path of the current folder. Folder::pwd() will return the same information.
property Folder::$sort
Whether or not the list results should be sorted by name.
property Folder::$mode
Mode to be used when creating folders. Defaults to 0755. Does nothing on Windows machines.

Utilities425

CakePHP Cookbook Documentation, Release 2.x

static Folder::addPathElement(string $path, string $element)
Return typestringReturns $path with $element added, with correct slash in-between:$path = Folder::addPathElement('/a/path/for', 'testing');// $path equals /a/path/for/testing$element can also be an array:$path = Folder::addPathElement('/a/path/for', array('testing', 'another'));// $path equals /a/path/for/testing/another
New in version 2.5: $element parameter accepts an array as of 2.5
Folder::cd(string $path)
Return typestringChange directory to $path. Returns false on failure:<?php$folder = new Folder('/foo');echo $folder->path; // Prints /foo
$folder->cd('/bar');echo $folder->path; // Prints /bar$false = $folder->cd('/non-existent-folder');
Folder::chmod(string $path, integer $mode = false, boolean $recursive = true, array $exceptions = array())
Return typebooleanChange the mode on a directory structure recursively. This includes changing the mode on files as well:<?php$dir = new Folder();$dir->chmod('/path/to/folder', 0755, true, array('skip_me.php'));Folder::copy(array|string $options = array())
Return typebooleanCopy a directory (recursively by default). The only parameter $options can either be a path into copy to or anarray of options:<?php$folder1 = new Folder('/path/to/folder1');$folder1->copy('/path/to/folder2');// Will put folder1 and all its contents into folder2$folder = new Folder('/path/to/folder');$folder->copy(array('to' => '/path/to/new/folder',(continues on next page)

426

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'from' => '/path/to/copy/from', // will cause a cd() to occur'mode' => 0755,'skip' => array('skip-me.php', '.git'),'scheme' => Folder::SKIP, // Skip directories/files that already exist.
'recursive' => true //set false to disable recursive copy));
There are 3 supported schemes:• Folder::SKIP skip copying/moving files & directories that exist in the destination directory.
• Folder::MERGE merge the source/destination directories. Files in the source directory will replace files inthe target directory. Directory contents will be merged.
• Folder::OVERWRITE overwrite existing files & directories in the target directory with those in the source
directory. If both the target and destination contain the same subdirectory, the target directory’s contents
will be removed and replaced with the source’s.
Changed in version 2.3: The merge, skip and overwrite schemes were added to copy()
static Folder::correctSlashFor(string $path)
Return typestringReturns a correct set of slashes for given $path (’\’ for Windows paths and ‘/’ for other paths).
Folder::create(string $pathname, integer $mode = false)
Return typebooleanCreate a directory structure recursively. Can be used to create deep path structures like /foo/bar/baz/shoe/horn:<?php$folder = new Folder();
if ($folder->create('foo' . DS . 'bar' . DS . 'baz' . DS . 'shoe' . DS . 'horn')) {// Successfully created the nested folders}
Folder::delete(string $path = null)
Return typeboolean
Recursively remove directories if the system allows:<?php$folder = new Folder('foo');if ($folder->delete()) {// Successfully deleted foo and its nested folders}
Folder::dirsize()
Return type
integer
Returns the size in bytes of this Folder and its contents.

Utilities427

CakePHP Cookbook Documentation, Release 2.x

Folder::errors()
Return typearray
Get the error from latest method.
Folder::find(string $regexpPattern = '.*', boolean $sort = false)
Return typearray
Returns an array of all matching files in the current directory:<?php// Find all .png in your app/webroot/img/ folder and sort the results$dir = new Folder(WWW_ROOT . 'img');$files = $dir->find('.*\.png', true);
/*
Array
(
[0] => cake.icon.png[1] => test-error-icon.png[2] => test-fail-icon.png[3] => test-pass-icon.png[4] => test-skip-icon.png)*/

Note: The folder find and findRecursive methods will only find files. If you would like to get folders and files see
Folder::read() or Folder::tree()
Folder::findRecursive(string $pattern = '.*', boolean $sort = false)
Return typearray
Returns an array of all matching files in and below the current directory:<?php// Recursively find files beginning with test or index$dir = new Folder(WWW_ROOT);$files = $dir->findRecursive('(test|index).*');
/*
Array
(
[0] => /var/www/cake/app/webroot/index.php[1] => /var/www/cake/app/webroot/test.php[2] => /var/www/cake/app/webroot/img/test-skip-icon.png[3] => /var/www/cake/app/webroot/img/test-fail-icon.png[4] => /var/www/cake/app/webroot/img/test-error-icon.png[5] => /var/www/cake/app/webroot/img/test-pass-icon.png
)
*/

428

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Folder::inCakePath(string $path = '')Return typebooleanReturns true if the file is in a given CakePath.
Folder::inPath(string $path = '', boolean $reverse = false)
Return typebooleanReturns true if the file is in the given path:<?php$Folder = new Folder(WWW_ROOT);$result = $Folder->inPath(APP);// $result = true, /var/www/example/app/ is in /var/www/example/app/webroot/$result = $Folder->inPath(WWW_ROOT . 'img' . DS, true);// $result = true, /var/www/example/app/webroot/ is in /var/www/example/app/webroot/
˓→img/
static Folder::isAbsolute(string $path)
Return typebooleanReturns true if the given $path is an absolute path.
static Folder::isSlashTerm(string $path)
Return typebooleanReturns true if given $path ends in a slash (i.e. is slash-terminated):<?php$result = Folder::isSlashTerm('/my/test/path');// $result = false$result = Folder::isSlashTerm('/my/test/path/');// $result = true
static Folder::isWindowsPath(string $path)
Return typebooleanReturns true if the given $path is a Windows path.
Folder::messages()
Return typearray
Get the messages from the latest method.
Folder::move(array $options)
Return typebooleanUtilities429

CakePHP Cookbook Documentation, Release 2.x

Move a directory (recursively by default). The only parameter $options is the same as for copy()
static Folder::normalizePath(string $path)
Return typestringReturns a correct set of slashes for given $path (’\’ for Windows paths and ‘/’ for other paths).
Folder::pwd()
Return typestringReturn current path.
Folder::read(boolean $sort = true, array|boolean $exceptions = false, boolean $fullPath = false)
Return type
mixed
Parameters• $sort (boolean) – If true will sort results.
• $exceptions (mixed) – An array of files and folder names to ignore. If true or ‘.’ thismethod will ignore hidden or dot files.
• $fullPath (boolean) – If true will return results using absolute paths.
Returns an array of the contents of the current directory. The returned array holds two sub arrays: One of
directories and one of files:<?php$dir = new Folder(WWW_ROOT);$files = $dir->read(true, array('files', 'index.php'));
/*
Array
(
[0] => Array // folders([0] => css[1] => img[2] => js)[1] => Array // files([0] => .htaccess[1] => favicon.ico[2] => test.php))*/
Folder::realpath(string $path)
Return typestringGet the real path (taking “..” and such into account).

430

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

static Folder::slashTerm(string $path)
Return typestringReturns $path with added terminating slash (corrected for Windows or other OS).
Folder::tree(null|string $path = null, array|boolean $exceptions = true, null|string $type = null)
Return type
mixed
Returns an array of nested directories and files in each directory.
File API
class File(string $path, boolean $create = false, integer $mode = 755)<?php// Create a new file with 0644 permissions$file = new File('/path/to/file.php', true, 0644);
property File::$Folder
The Folder object of the file.
property File::$name
The name of the file with the extension. Differs from File::name() which returns the name without the extension.
property File::$info
An array of file info. Use File::info() instead.
property File::$handle
Holds the file handler resource if the file is opened.
property File::$lock
Enable locking for file reading and writing.
property File::$path
The current file’s absolute path.
File::append(string $data, boolean $force = false)
Return typebooleanAppend the given data string to the current file.
File::close()
Return typebooleanCloses the current file if it is opened.
File::copy(string $dest, boolean $overwrite = true)
Return typebooleanUtilities

431

CakePHP Cookbook Documentation, Release 2.x

Copy the file to $dest.
File::create()
Return typebooleanCreates the file.
File::delete()
Return typebooleanDeletes the file.
File::executable()
Return typebooleanReturns true if the file is executable.
File::exists()
Return typebooleanReturns true if the file exists.
File::ext()
Return typestringReturns the file extension.
File::Folder()Return type
FolderReturns the current folder.
File::group()
Return type
integer|false
Returns the file’s group, or false in case of an error.
File::info()
Return typearray
Returns the file info.
Changed in version 2.1: File::info() now includes filesize & mimetype information.
File::lastAccess()
Return type
integer|false
Returns last access time, or false in case of an error.

432

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

File::lastChange()
Return type
integer|false
Returns last modified time, or false in case of an error.
File::md5(integer|boolean $maxsize = 5)
Return typestringGet the MD5 Checksum of file with previous check of filesize, or false in case of an error.
File::name()
Return typestringReturns the file name without extension.
File::offset(integer|boolean $offset = false, integer $seek = 0)
Return type
mixed
Sets or gets the offset for the currently opened file.
File::open(string $mode = 'r', boolean $force = false)
Return typebooleanOpens the current file with the given $mode.
File::owner()
Return type
integer
Returns the file’s owner.
File::perms()
Return typestringReturns the “chmod” (permissions) of the file.
static File::prepare(string $data, boolean $forceWindows = false)
Return typestring
Prepares a ascii string for writing. Converts line endings to the correct terminator for the current platform. For
Windows “rn” will be used, “n” for all other platforms.
File::pwd()
Return typestringReturns the full path of the file.

Utilities433

CakePHP Cookbook Documentation, Release 2.x

File::read(string $bytes = false, string $mode = 'rb', boolean $force = false)
Return typestring|boolean
Return the contents of the current file as a string or return false on failure.
File::readable()
Return typebooleanReturns true if the file is readable.
File::safe(string $name = null, string $ext = null)
Return typestringMakes filename safe for saving.
File::size()
Return type
integer
Returns the filesize.
File::writable()
Return typebooleanReturns true if the file is writable.
File::write(string $data, string $mode = 'w', boolean$force = false)
Return typebooleanWrite given data to the current file.
New in version 2.1: File::mime()
File::mime()
Return typemixed
Get the file’s mimetype, returns false on failure.
File::replaceText($search, $replace)
Return typebooleanReplaces text in a file. Returns false on failure and true on success.
New in version 2.5: File::replaceText()

434

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Hashclass Hash
New in version 2.2.
Array management, if done right, can be a very powerful and useful tool for building smarter, more optimized code.
CakePHP offers a very useful set of static utilities in the Hash class that allow you to do just that.
CakePHP’s Hash class can be called from any model or controller in the same way Inflector is called. Example:
Hash::combine().
Hash path syntax
The path syntax described below is used by all the methods in Hash. Not all parts of the path syntax are available inall methods. A path expression is made of any number of tokens. Tokens are composed of two groups. Expressions,
are used to traverse the array data, while matchers are used to qualify elements. You apply matchers to expression
elements.
Expression Types

Expression

Definition

{n}
{s}
{*}
Foo

Represents a numeric key. Will match any string or numeric key.
Represents a string. Will match any string value including numeric string values.
Represents any value regardless of type.
Matches keys with the exact same value.

All expression elements are supported by all methods. In addition to expression elements, you can use attribute matching with certain methods. They are extract(), combine(), format(), check(), map(), reduce(), apply(),sort(), insert(), remove() and nest().
Attribute Matching Types

MatcherDefinition[id][id=2][id!=2][id>2][id>=2][id<2][id<=2][text=/.../]

Match elements with a given array key.
Match elements with id equal to 2.
Match elements with id not equal to 2.
Match elements with id greater than 2.
Match elements with id greater than or equal to 2.
Match elements with id less than 2
Match elements with id less than or equal to 2.
Match elements that have values matching the regular expression inside ....

Use matchers by appending them to the expression element ({n}, {s}, etc.) you wish to match.
So to return id fields where a name matches you can use paths using {n} and {s} to insert data into multiple points:

Utilities435

CakePHP Cookbook Documentation, Release 2.x$users = Array(
Array('id' => 123,
'name'=> 'fred','surname' => 'bloggs'),
Array('id' => 245,
'name' => 'fred','surname' => 'smith'),Array('id' => 356,
'name' => 'joe','surname' => 'smith'));$ids = Hash::extract($users, '{n}[name=fred].id');// $ids will be array (123, 245)
Changed in version 2.5: Matcher support was added to insert() and remove().
static Hash::get(array $data, $path, $default = null)
Return type
mixed
get() is a simplified version of extract(), it only supports direct path expressions. Paths with {n}, {s} ormatchers are not supported. Use get() when you want exactly one value out of an array. The optional thirdargument will be returned if the requested path is not found in the array.
Changed in version 2.5: The optional third argument $default = null was added.
static Hash::extract(array $data, $path)
Return typearray
Hash::extract() supports all expression, and matcher components of Hash path syntax. You can use extract
to retrieve data from arrays, along arbitrary paths quickly without having to loop through the data structures.
Instead you use path expressions to qualify which elements you want returned// Common Usage:$users = $this->User->find("all");$results = Hash::extract($users, '{n}.User.id');// $results equals:// array(1,2,3,4,5,...);
static Hash::insert(array $data, $path, $values = null)
Return type
array
Inserts $data into an array as defined by $path:$a = array('pages' => array('name' => 'page')(continues on next page)

436

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page));$result = Hash::insert($a, 'files', array('name' => 'files'));// $result now looks like:Array([pages] => Array([name] => page)
[files] => Array([name] => files))
You can use paths using {n} and {s} to insert data into multiple points:$users = $this->User->find('all');$users = Hash::insert($users, '{n}.User.new', 'value');
Changed in version 2.5: As of 2.5.0 attribute matching expressions work with insert().
static Hash::remove(array $data, $path)
Return typearray
Removes all elements from an array that match $path.
$a = array('pages' => array('name' => 'page'),'files' => array('name' => 'files'));$result = Hash::remove($a, 'files');/* $result now looks like:Array([pages] => Array([name] => page))*/
Using {n} and {s} will allow you to remove multiple values at once.
Changed in version 2.5: As of 2.5.0 attribute matching expressions work with remove()static Hash::combine(array $data, $keyPath, $valuePath = null, $groupPath = null)
Return typearray
Creates an associative array using a $keyPath as the path to build its keys, and optionally $valuePath as path to
get the values. If $valuePath is not specified, or doesn’t match anything, values will be initialized to null. You

Utilities437

CakePHP Cookbook Documentation, Release 2.x

can optionally group the values by what is obtained when following the path specified in $groupPath.
$a = array(
array('User' => array('id' => 2,'group_id' => 1,'Data' => array('user' => 'mariano.iglesias','name' => 'Mariano Iglesias'))),
array('User' => array('id' => 14,'group_id' => 2,'Data' => array('user' => 'phpnut','name' => 'Larry E. Masters'
)
)
),
);
$result = Hash::combine($a, '{n}.User.id');/* $result now looks like:Array([2] =>[14] =>)*/$result = Hash::combine($a, '{n}.User.id', '{n}.User.Data');/* $result now looks like:Array([2] => Array([user] => mariano.iglesias[name] => Mariano Iglesias)[14] => Array([user] => phpnut[name] => Larry E. Masters))*/$result = Hash::combine($a, '{n}.User.id', '{n}.User.Data.name');/* $result now looks like:(continues on next page)

438

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)Array([2] => Mariano Iglesias[14] => Larry E. Masters)*/$result = Hash::combine($a, '{n}.User.id', '{n}.User.Data', '{n}.User.group_id');/* $result now looks like:Array([1] => Array([2] => Array([user] => mariano.iglesias[name] => Mariano Iglesias))[2] => Array([14] => Array([user] => phpnut[name] => Larry E. Masters)))*/$result = Hash::combine($a, '{n}.User.id', '{n}.User.Data.name', '{n}.User.group_id
˓→');/* $result now looks like:Array([1] => Array([2] => Mariano Iglesias)[2] => Array([14] => Larry E. Masters))*/
You can provide array’s for both $keyPath and $valuePath. If you do this, the first value will be used as a format
string, for values extracted by the other paths:$result = Hash::combine($a,'{n}.User.id',(continues on next page)

Utilities439

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)array('%s: %s', '{n}.User.Data.user', '{n}.User.Data.name'),'{n}.User.group_id');/* $result now looks like:Array([1] => Array([2] => mariano.iglesias: Mariano Iglesias)[2] => Array([14] => phpnut: Larry E. Masters))*/$result = Hash::combine($a,array('%s: %s', '{n}.User.Data.user', '{n}.User.Data.name'),'{n}.User.id');/* $result now looks like:Array([mariano.iglesias: Mariano Iglesias] => 2
[phpnut: Larry E. Masters] => 14)*/
static Hash::format(array $data, array $paths, $format)
Return typearray
Returns a series of values extracted from an array, formatted with a format string:$data = array(
array('Person' => array('first_name' => 'Nate','last_name' => 'Abele','city' => 'Boston','state' => 'MA','something' => '42')),
array('Person' => array('first_name' => 'Larry','last_name' => 'Masters','city' => 'Boondock','state' => 'TN',(continues on next page)

440

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'something' => '{0}')),array('Person' => array('first_name' => 'Garrett','last_name' => 'Woodworth','city' => 'Venice Beach','state' => 'CA','something' => '{1}')));$res = Hash::format($data, array('{n}.Person.first_name', '{n}.Person.something'), '˓→%2$d, %1$s');
/*
Array
(
[0] => 42, Nate[1] => 0, Larry[2] => 0, Garrett)*/$res = Hash::format($data, array('{n}.Person.first_name', '{n}.Person.something'), '˓→%1$s, %2$d');
/*
Array
(
[0] => Nate, 42[1] => Larry, 0[2] => Garrett, 0)*/static Hash::contains(array $data, array $needle)
Return typebooleanDetermines if one Hash or array contains the exact keys and values of another:$a = array(0 => array('name' => 'main'),
1 => array('name' => 'about'));$b = array(0 => array('name' => 'main'),
1 => array('name' => 'about'),
2 => array('name' => 'contact'),'a' => 'b');(continues on next page)

Utilities

441

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$result = Hash::contains($a, $a);// true$result = Hash::contains($a, $b);// false$result = Hash::contains($b, $a);// truestatic Hash::check(array $data, string $path = null)
Return typebooleanChecks if a particular path is set in an array:$set = array('My Index 1' => array('First' => 'The first item'));$result = Hash::check($set, 'My Index 1.First');// $result == True
$result = Hash::check($set, 'My Index 1');// $result == True
$set = array('My Index 1' => array('First' =>array('Second' =>array('Third' =>array('Fourth' => 'Heavy. Nesting.')))));$result = Hash::check($set, 'My Index 1.First.Second');// $result == True
$result = Hash::check($set, 'My Index 1.First.Second.Third');// $result == True
$result = Hash::check($set, 'My Index 1.First.Second.Third.Fourth');// $result == True
$result = Hash::check($set, 'My Index 1.First.Seconds.Third.Fourth');// $result == False
static Hash::filter(array $data, $callback = array('Hash', 'filter'))
Return typearray
Filters empty elements out of array, excluding ‘0’. You can also supply a custom $callback to filter the arrayelements. Your callback should return false to remove elements from the resulting array:$data = array('0',false,
true,(continues on next page)

442

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)0,
array('one thing', 'I can tell you', 'is you got to be', false));$res = Hash::filter($data);/* $data now looks like:
Array (
[0] => 0[2] => true[3] => 0[4] => Array([0] => one thing[1] => I can tell you[2] => is you got to be))*/static Hash::flatten(array $data, string $separator = '.')
Return typearray
Collapses a multi-dimensional array into a single dimension:$arr = array(
array('Post' => array('id' => '1', 'title' => 'First Post'),'Author' => array('id' => '1', 'user' => 'Kyle'),),array('Post' => array('id' => '2', 'title' => 'Second Post'),'Author' => array('id' => '3', 'user' => 'Crystal'),),);$res = Hash::flatten($arr);/* $res now looks like:
Array (
[0.Post.id] => 1
[0.Post.title] => First Post
[0.Author.id] => 1
[0.Author.user] => Kyle
[1.Post.id] => 2
[1.Post.title] => Second Post
[1.Author.id] => 3
[1.Author.user] => Crystal)*/
static Hash::expand(array $data, string $separator = '.')
Return typearray

Utilities443

CakePHP Cookbook Documentation, Release 2.x

Expands an array that was previously flattened with Hash::flatten():$data = array('0.Post.id' => 1,'0.Post.title' => First Post,'0.Author.id' => 1,'0.Author.user' => Kyle,'1.Post.id' => 2,'1.Post.title' => Second Post,'1.Author.id' => 3,'1.Author.user' => Crystal,);$res = Hash::expand($data);/* $res now looks like:array(
array('Post' => array('id' => '1', 'title' => 'First Post'),'Author' => array('id' => '1', 'user' => 'Kyle'),),array('Post' => array('id' => '2', 'title' => 'Second Post'),'Author' => array('id' => '3', 'user' => 'Crystal'),),);*/static Hash::merge(array $data, array $merge[, array $n ])Return typearray
This function can be thought of as a hybrid between PHP’s array_merge and array_merge_recursive. The
difference to the two is that if an array key contains another array then the function behaves recursive (unlike
array_merge) but does not do if for keys containing strings (unlike array_merge_recursive).
Note: This function will work with an unlimited amount of arguments and typecasts non-array parameters into
arrays.
$array = array(
array('id' => '48c2570e-dfa8-4c32-a35e-0d71cbdd56cb','name' => 'mysql raleigh-workshop-08 < 2008-09-05.sql ','description' => 'Importing an sql dump'),
array('id' => '48c257a8-cf7c-4af2-ac2f-114ecbdd56cb','name' => 'pbpaste | grep -i Unpaid | pbcopy','description' => 'Remove all lines that say "Unpaid".',));$arrayB = 4;
$arrayC = array(0 => "test array", "cats" => "dogs", "people" => 1267);$arrayD = array("cats" => "felines", "dog" => "angry");(continues on next page)444

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$res = Hash::merge($array, $arrayB, $arrayC, $arrayD);/* $res now looks like:Array([0] => Array([id] => 48c2570e-dfa8-4c32-a35e-0d71cbdd56cb[name] => mysql raleigh-workshop-08 < 2008-09-05.sql[description] => Importing an sql dump)
[1] => Array([id] => 48c257a8-cf7c-4af2-ac2f-114ecbdd56cb[name] => pbpaste | grep -i Unpaid | pbcopy[description] => Remove all lines that say "Unpaid".
)[2] => 4[3] => test array[cats] => felines[people] => 1267
[dog] => angry)*/
static Hash::numeric(array $data)
Return typebooleanChecks to see if all the values in the array are numeric:$data = array('one');$res = Hash::numeric(array_keys($data));// $res is true
$data = array(1 => 'one');$res = Hash::numeric($data);// $res is false
static Hash::dimensions(array $data)
Return type
integerCounts the dimensions of an array. This method will only consider the dimension of the first element in the array:$data = array('one', '2', 'three');$result = Hash::dimensions($data);// $result == 1
$data = array('1' => '1.1', '2', '3');$result = Hash::dimensions($data);// $result == 1
(continues on next page)

Utilities445

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => '3.1.1'));$result = Hash::dimensions($data);// $result == 2$data = array('1' => '1.1', '2', '3' => array('3.1' => '3.1.1'));$result = Hash::dimensions($data);// $result == 1
$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => array('3.1.
˓→1' => '3.1.1.1')));$result = Hash::dimensions($data);// $result == 2
static Hash::maxDimensions(array $data)
Similar to dimensions(), however this method returns, the deepest number of dimensions of any element in
the array:$data = array('1' => '1.1', '2', '3' => array('3.1' => '3.1.1'));$result = Hash::maxDimensions($data);// $result == 2$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => array('3.1.
˓→1' => '3.1.1.1')));$result = Hash::maxDimensions($data);// $result == 3
static Hash::map(array $data, $path, $function)
Creates a new array, by extracting $path, and mapping $function across the results. You can use both expression
and matching elements with this method:// Call the noop function $this->noop() on every element of $data$result = Hash::map($data, "{n}", array($this, 'noop'));function noop($array) {// Do stuff to array and return the result
return $array;}static Hash::reduce(array $data, $path, $function)
Creates a single value, by extracting $path, and reducing the extracted results with $function. You can use both
expression and matching elements with this method.
static Hash::apply(array $data, $path, $function)
Apply a callback to a set of extracted values using $function. The function will get the extracted values as thefirst argument.
static Hash::sort(array $data, $path, $dir, $type = 'regular')
Return typearray
Sorts an array by any value, determined by a Hash path syntax Only expression elements are supported by thismethod:446

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x$a = array(0 => array('Person' => array('name' => 'Jeff')),
1 => array('Shirt' => array('color' => 'black')));$result = Hash::sort($a, '{n}.Person.name', 'asc');/* $result now looks like:Array([0] => Array([Shirt] => Array([color] => black))[1] => Array([Person] => Array([name] => Jeff)))*/
$dir can be either asc or desc. $type can be one of the following values:• regular for regular sorting.
• numeric for sorting values as their numeric equivalents.
• string for sorting values as their string value.
• natural for sorting values in a human friendly way. Will sort foo10 below foo2 as an example. Naturalsorting requires PHP 5.4 or greater.
New in version 2.8: The $type option now supports an array and the ignoreCase option enabled caseinsensitive sorting.
static Hash::diff(array $data, array $compare)
Return typearray
Computes the difference between two arrays:$a = array(0 => array('name' => 'main'),
1 => array('name' => 'about'));$b = array(0 => array('name' => 'main'),
1 => array('name' => 'about'),
2 => array('name' => 'contact'));$result = Hash::diff($a, $b);(continues on next page)

Utilities447

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)/* $result now looks like:Array([2] => Array([name] => contact))*/
static Hash::mergeDiff(array $data, array $compare)
Return typearray
This function merges two arrays and pushes the differences in data to the bottom of the resultant array.
Example 1$array1 = array('ModelOne' => array('id' => 1001, 'field_one' => 'a1.m1.f1', 'field_
˓→two' => 'a1.m1.f2'));$array2 = array('ModelOne' => array('id' => 1003, 'field_one' => 'a3.m1.f1', 'field_
˓→two' => 'a3.m1.f2', 'field_three' => 'a3.m1.f3'));$res = Hash::mergeDiff($array1, $array2);/* $res now looks like:
Array([ModelOne] => Array([id] => 1001
[field_one] => a1.m1.f1[field_two] => a1.m1.f2[field_three] => a3.m1.f3))*/
Example 2$array1 = array("a" => "b", 1 => 20938, "c" => "string");$array2 = array("b" => "b", 3 => 238, "c" => "string", array("extra_field"));$res = Hash::mergeDiff($array1, $array2);/* $res now looks like:Array([a] => b[1] => 20938[c] => string[b] => b[3] => 238[4] => Array([0] => extra_field(continues on next page)

448

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)))*/
static Hash::normalize(array $data, $assoc = true)
Return typearray
Normalizes an array. If $assoc is true, the resulting array will be normalized to be an associative array. Numerickeys with values, will be converted to string keys with null values. Normalizing an array, makes using the resultswith Hash::merge() easier:$a = array('Tree', 'CounterCache','Upload' => array('folder' => 'products','fields' => array('image_1_id', 'image_2_id')));$result = Hash::normalize($a);/* $result now looks like:
Array([Tree] => null[CounterCache] => null
[Upload] => Array([folder] => products[fields] => Array([0] => image_1_id[1] => image_2_id)))*/$b = array('Cacheable' => array('enabled' => false),'Limit','Bindable','Validator','Transactional');$result = Hash::normalize($b);/* $result now looks like:
Array([Cacheable] => Array([enabled] => false)(continues on next page)

Utilities449

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[Limit] => null
[Bindable] => null[Validator] => null[Transactional] => null)*/static Hash::nest(array $data, array $options = array())
Takes a flat array set, and creates a nested, or threaded data structure.
Model::find('threaded').

Used by methods like

Options:• children The key name to use in the result set for children. Defaults to ‘children’.
• idPath The path to a key that identifies each entry. Should be compatible with Hash::extract(). Defaults to {n}.$alias.id• parentPath The path to a key that identifies the parent of each entry. Should be compatible with
Hash::extract(). Defaults to {n}.$alias.parent_id• root The id of the desired top-most result.
Example:$data = array(
array('ModelName' => array('id' => 1, 'parent_id' => null)),array('ModelName' => array('id' => 2, 'parent_id' => 1)),array('ModelName' => array('id' => 3, 'parent_id' => 1)),array('ModelName' => array('id' => 4, 'parent_id' => 1)),array('ModelName' => array('id' => 5, 'parent_id' => 1)),array('ModelName' => array('id' => 6, 'parent_id' => null)),array('ModelName' => array('id' => 7, 'parent_id' => 6)),array('ModelName' => array('id' => 8, 'parent_id' => 6)),array('ModelName' => array('id' => 9, 'parent_id' => 6)),array('ModelName' => array('id' => 10, 'parent_id' => 6)));$result = Hash::nest($data, array('root' => 6));/* $result now looks like:array((int) 0 => array('ModelName' => array('id' => (int) 6,'parent_id' => null),'children' => array((int) 0 => array('ModelName' => array('id' => (int) 7,'parent_id' => (int) 6),'children' => array()),(int) 1 => array((continues on next page)

450

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'ModelName' => array('id' => (int) 8,'parent_id' => (int) 6),'children' => array()),(int) 2 => array('ModelName' => array('id' => (int) 9,'parent_id' => (int) 6),'children' => array()),(int) 3 => array('ModelName' => array('id' => (int) 10,'parent_id' => (int) 6),'children' => array()
)
)
)
)
*/

HttpSocketclass HttpSocket(mixed $config = array())
CakePHP includes an HttpSocket class which can be used easily for making requests. It is a great way to communicate
with external webservices, or remote APIs.
Making a request
You can use HttpSocket to create most kinds of HTTP requests with the different HTTP methods.
HttpSocket::get($uri, $query, $request)
The $query parameter, can either be a query string, or an array of keys and values. The get method makes asimple HTTP GET request returning the results:
App::uses('HttpSocket', 'Network/Http');$HttpSocket = new HttpSocket();// string query$results = $HttpSocket->get('https://www.google.com/search', 'q=cakephp');// array query$results = $HttpSocket->get('https://www.google.com/search', array('q' => 'cakephp
˓→'));

Utilities

451

CakePHP Cookbook Documentation, Release 2.x

HttpSocket::post($uri, $data, $request)
The post method makes a simple HTTP POST request returning the results.
The parameters for the post method are almost the same as the get method, $uri is the web address where therequest is being made; $data is the data to be posted, either as a string, or as an array of keys and values:
App::uses('HttpSocket', 'Network/Http');$HttpSocket = new HttpSocket();// string data
$results = $HttpSocket->post('http://example.com/add','name=test&type=user');// array data
$data = array('name' => 'test', 'type' => 'user');$results = $HttpSocket->post('http://example.com/add', $data);
HttpSocket::put($uri, $data, $request)
The put method makes a simple HTTP PUT request returning the results.
The parameters for the put method is the same as the post() method.
HttpSocket::delete($uri, $query, $request)
The delete method makes a simple HTTP DELETE request returning the results.
The parameters for the delete method is the same as the get() method. The $query parameter can either be
a string or an array of query string arguments for the request.
HttpSocket::patch($uri, $data, $request)
The patch method makes a simple HTTP PATCH request returning the results.
The parameters for the patch method is the same as the post() method.
New in version 2.4.
HttpSocket::request($request)
The base request method, which is called from all the wrappers (get, post, put, delete). Returns the results of therequest.
$request is a keyed array of various options. Here is the format and default settings:
public $request = array('method' => 'GET','uri' => array('scheme' => 'http','host' => null,'port' => 80,'user' => null,'pass' => null,'path' => null,'query' => null,'fragment' => null),'auth' => array((continues on next page)452

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'method' => 'Basic','user' => null,'pass' => null),'version' => '1.1','body' => '','line' => null,'header' => array('Connection' => 'close','User-Agent' => 'CakePHP'),'raw' => null,'redirect' => false,'cookies' => array());

Handling the response
Responses from requests made with HttpSocket are instances of HttpResponse. This object gives you a few accessor
methods to access the contents of an HTTP response. This class implements the ArrayAccess78 and __toString()79 , so
you can continue using the $http->response as array and the return of request methods as string:
App::uses('HttpSocket', 'Network/Http');$http = new HttpSocket();$response = $http->get('https://cakephp.org');// Check the body for the presence of a title tag.
$titlePos = strpos($response->body, '<title>');// Get the status code for the response.
$code = $response->code;
The HttpResponse has the following attributes:• body returns body of HTTP response (normally the HTML).
• headers returns array with headers.
• cookies returns array with new cookies (cookies from others request are not stored here).
• httpVersion returns string with HTTP version (from first line in response).
• code returns the integer with HTTP code.
• reasonPhrase returns the string with HTTP code response.
• raw returns the unchanged response from server.
The HttpResponse also exposes the following methods:• body() returns the body
• isOk() returns if code is 200;
78 https://www.php.net/manual/en/class.arrayaccess.php

79 https://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring

Utilities453

CakePHP Cookbook Documentation, Release 2.x• isRedirect() returns if code is 301, 302, 303 or 307 and the Location header is set.
• getHeader() allows you to fetch headers, see the next section.
Getting headers from a response
Following others places in core, the HttpSocket does not change the casing of headers. RFC 261680 states that headers
are case insensitive, and HttpSocket preserves the values the remote host sends:
HTTP/1.1 200 OK
Date: Mon, 16 Apr 2007 04:14:16 GMT
server: CakeHttp Server
content-tyPe: text/html
Your $response->headers (or $response['header']) will contain the exact keys sent. In order to safely accessthe header fields, it’s best to use getHeader(). If your headers looks like:
Date: Mon, 16 Apr 2007 04:14:16 GMT
server: CakeHttp Server
content-tyPe: text/html
You could fetch the above headers by calling:// $response is an instance of HttpResponse// get the Content-Type header.
$response->getHeader('Content-Type');// get the date
$response->getHeader('date');Headers can be fetched case-insensitively.
Automatically handling a redirect response
When the response has a valid redirect status code (see HttpResponse::isRedirect), an extra request can be automatically done according to the received Location header:<?php
App::uses('HttpSocket', 'Network/Http');$HttpSocket = new HttpSocket();$response = $HttpSocket->get('http://example.com/redirecting_url', array(), array(
˓→'redirect' => true));
The redirect option can take the following values• true : all redirecting responses will fire a consequent new request• integer : the set value is the maximum number of redirections allowed (after reaching it, the redirect value isconsidered as false)• false (default) : no consequent request will be fired
The returned $response will be the final one, according to the settings.
80 https://datatracker.ietf.org/doc/html/rfc2616.html

454

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Handling SSL certificates
When making requests to SSL services HttpSocket will attempt to validate the SSL certifcate using peer validation.
If the certificate fails peer validation or does not match the hostname being accessed the connection will fail, and an
exception will be thrown. By default HttpSocket will use the mozilla certificate authority file to verify SSL certificates.
You can use the following options to configure how SSL certificates are handled:• ssl_verify_peer Set to false to disable SSL verification. This is not recommended.
• ssl_verify_host Set to false if you wish to ignore hostname match errors when validating certificates.
• ssl_allow_self_signed Set to true to enable self-signed certificates to be accepted.
ssl_verify_peer to be enabled.

This requires• ssl_cafile Set to the absolute path of the Certificate Authority file that you wish to use for verifying SSL
certificates.
These options are provided as constructor arguments:$socket = new HttpSocket(array('ssl_allow_self_signed' => true));
Would allow self-signed certificates for all requests made with the created socket.
New in version 2.3: SSL certificate validation was added in 2.3.
Creating a custom response class
You can create your own response class to use with HttpSocket. You could create the file app/Lib/Network/Http/
YourResponse.php with the content:
App::uses('HttpResponse', 'Network/Http');class YourResponse extends HttpResponse {public function parseResponse($message) {parent::parseResponse($message);// Make what you want}}
Before your request you’ll need to change the responseClass property:
App::uses('HttpSocket', 'Network/Http');$http = new HttpSocket();$http->responseClass = 'YourResponse';
Changed in version 2.3: As of 2.3.0 you should extend HttpSocketResponse instead. This avoids a common issue
with the HTTP PECL extension.

Utilities455

CakePHP Cookbook Documentation, Release 2.x

Downloading the results
HttpSocket has a new method called setContentResource(). By setting a resource with this method, the content will be
written to this resource, using fwrite(). To you download a file, you can do:
App::uses('HttpSocket', 'Network/Http');$http = new HttpSocket();$f = fopen(TMP . 'bakery.xml', 'w');$http->setContentResource($f);$http->get('https://bakery.cakephp.org/comments.rss');
fclose($f);

Note: The headers are not included in file, you will only get the body content written to your resource. To disablesaving into the resource, use $http->setContentResource(false).

Using authentication
HttpSocket supports a HTTP Basic and Digest authentication methods out of the box. You can also create custom
authentication objects to support protocols like OAuth. To use any authentication system you need to configure the
HttpSocket instance:
App::uses('HttpSocket', 'Network/Http');$http = new HttpSocket();$http->configAuth('Basic', 'user', 'password');
The above would configure the HttpSocket instance to use Basic authentication using user and password as thecredentials.
Creating a custom authentication object
You can now create your own authentication method to use with HttpSocket. You could create the file app/Lib/
Network/Http/YourMethodAuthentication.php with the content:
class YourMethodAuthentication {/*** Authentication** @param HttpSocket $http* @param array $authInfo* @return void*/
public static function authentication(HttpSocket $http, &$authInfo) {// Do something, for example set $http->request['header']['Authentication'] value
}
}

456

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

To configure HttpSocket to use your auth configuration, you can use the new method configAuth():$http->configAuth('YourMethod', array('config1' => 'value1', 'config2' => 'value2'));$http->get('http://secure.your-site.com');
The authentication() method will be called to append the request headers.
Using a HttpSocket with a proxy
As part of auth configuration, you can configure proxy authentication. You can create your customized method to proxyauthentication in the same class of authentication. For example:
class YourMethodAuthentication {/*** Authentication** @param HttpSocket $http* @param array $authInfo* @return void*/
public static function authentication(HttpSocket $http, &$authInfo) {// Do something, for example set $http->request['header']['Authentication'] value}/*** Proxy Authentication** @param HttpSocket $http* @param array $proxyInfo* @return void*/
public static function proxyAuthentication(HttpSocket $http, &$proxyInfo) {// Do something, for example set $http->request['header']['Proxy-Authentication']␣
˓→value}}

Note: To use a proxy, you must call the HttpSocket::configProxy() similar to HttpSocket::configAuth().

Inflector
class Inflector
The Inflector class takes a string and can manipulate it to handle word variations such as pluralizations or camelizing
and is normally accessed statically. Example: Inflector::pluralize('example') returns “examples”.
You can try out the inflections online at inflector.cakephp.org81 .
81 https://inflector.cakephp.org/

Utilities457

CakePHP Cookbook Documentation, Release 2.x

static Inflector::pluralize($singular)• Input: Apple, Orange, Person, Man• Output: Apples, Oranges, People, Men
Note: pluralize() may not always correctly convert a noun that is already in it’s plural form.
static Inflector::singularize($plural)• Input: Apples, Oranges, People, Men• Output: Apple, Orange, Person, Man
Note: singularize() may not always correctly convert a noun that is already in it’s singular form.
static Inflector::camelize($underscored)• Input: Apple_pie, some_thing, people_person• Output: ApplePie, SomeThing, PeoplePerson
static Inflector::underscore($camelCase)
It should be noted that underscore will only convert camelCase formatted words. Words that contains spaces will
be lower-cased, but will not contain an underscore.
• Input: applePie, someThing• Output: apple_pie, some_thingstatic Inflector::humanize($underscored)• Input: apple_pie, some_thing, people_person• Output: Apple Pie, Some Thing, People Person
static Inflector::tableize($camelCase)• Input: Apple, UserProfileSetting, Person• Output: apples, user_profile_settings, people
static Inflector::classify($underscored)• Input: apples, user_profile_settings, people• Output: Apple, UserProfileSetting, Person
static Inflector::variable($underscored)• Input: apples, user_result, people_people• Output: apples, userResult, peoplePeople
static Inflector::slug($word, $replacement = '_')
Slug converts special characters into latin versions and converting unmatched characters and spaces to underscores. The slug method expects UTF-8 encoding.
• Input: apple purée• Output: apple_puree

458

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

static Inflector::reset
Resets Inflector back to its initial state, useful in testing.
static Inflector::rules($type, $rules, $reset = false)
Define new inflection and transliteration rules for Inflector to use. See Inflection Configuration for more information.
Internationalization & Localization
One of the best ways for your applications to reach a larger audience is to cater for multiple languages. This can often
prove to be a daunting task, but the internationalization and localization features in CakePHP make it much easier.
First, it’s important to understand some terminology. Internationalization refers to the ability of an application to
be localized. The term localization refers to the adaptation of an application to meet specific language (or culture)requirements (i.e., a “locale”). Internationalization and localization are often abbreviated as i18n and l10n respectively;
18 and 10 are the number of characters between the first and last character.
Internationalizing Your Application
There are only a few steps to go from a single-language application to a multi-lingual application, the first of which is
to make use of the __() function in your code. Below is an example of some code for a single-language application:<h2>Posts</h2>
To internationalize your code, all you need to do is to wrap strings in __() like so:<h2><?php echo __('Posts'); ?></h2>
If you do nothing further, these two code examples are functionally identical - they will both send the same content tothe browser. The __() function will translate the passed string if a translation is available, or return it unmodified. It
works similar to other Gettext82 implementations (as do the other translate functions, such as __d() , __n() etc)
With your code ready to be multilingual, the next step is to create your pot file83 , which is the template for all translatablestrings in your application. To generate your pot file(s), all you need to do is run the i18n console task, which will lookfor where you’ve used a translate function in your code and generate your pot file(s) for you. You can and should re-run
this console task any time you change the translations in your code.
The pot file(s) themselves are not used by CakePHP, they are the templates used to create or update your po files84 ,which contain the translations. CakePHP will look for your po files in the following location:/app/Locale/<locale>/LC_MESSAGES/<domain>.po
The default domain is ‘default’, therefore your locale folder would look something like this:/app/Locale/eng/LC_MESSAGES/default.po (English)/app/Locale/fra/LC_MESSAGES/default.po (French)/app/Locale/por/LC_MESSAGES/default.po (Portuguese)
To create or edit your po files it’s recommended that you do not use your favorite editor. To create a po file for the firsttime it is possible to copy the pot file to the correct location and change the extension however unless you’re familiar
with their format, it’s quite easy to create an invalid po file or to save it as the wrong charset (if you’re editing manually,
82 https://en.wikipedia.org/wiki/Gettext
83 https://en.wikipedia.org/wiki/Gettext
84 https://en.wikipedia.org/wiki/Gettext

Utilities459

CakePHP Cookbook Documentation, Release 2.x

use UTF-8 to avoid problems). There are free tools such as PoEdit85 which make editing and updating your po files aneasy task; especially for updating an existing po file with a newly updated pot file.
The three-character locale codes conform to the ISO 639-286 standard, although if you create regional locales (en_US,
en_GB, etc.) cake will use them if appropriate.
Warning: In 2.3 and 2.4 some language codes have been corrected to meet the ISO standard. Please see thecorresponding migration guides for details.
Remember that po files are useful for short messages, if you find you want to translate long paragraphs, or even wholepages - you should consider implementing a different solution. e.g.
// App Controller Code.
public function beforeFilter() {$locale = Configure::read('Config.language');if ($locale && file_exists(APP . 'View' . DS . $locale . DS . $this->viewPath . DS .
˓→$this->view . $this->ext)) {// e.g. use /app/View/fra/Pages/tos.ctp instead of /app/View/Pages/tos.ctp$this->viewPath = $locale . DS . $this->viewPath;}}or:// View codeecho $this->element(Configure::read('Config.language') . '/tos');
For translation of strings of LC_TIME category CakePHP uses POSIX compliant LC_TIME files. The i18n functionsof CakeTime utility class and helper TimeHelper use these LC_TIME files.
Just place LC_TIME file in its respective locale directory:/app/Locale/fra/LC_TIME (French)/app/Locale/por/LC_TIME (Portuguese)
You can find these files for few popular languages from the official Localized87 repo.
Internationalizing CakePHP Plugins
If you want to include translation files within your application you’ll need to follow a few conventions.
Instead of __() and __n() you will have to use __d() and __dn(). The D means domain. So if you have a plugin called‘DebugKit’ you would have to do this:__d('debug_kit', 'My example text');
Using the underscored syntax is important, if you don’t use it CakePHP won’t find your translation file.
Your translation file for this example should go into:
85 https://www.poedit.net

86 https://www.loc.gov/standards/iso639-2/php/code_list.php
87 https://github.com/cakephp/localized

460

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x/app/Plugin/DebugKit/Locale/<locale>/LC_MESSAGES/<domain>.po
And for other languages than the default:/app/Plugin/DebugKit/Locale/eng/LC_MESSAGES/debug_kit.po (English)/app/Plugin/DebugKit/Locale/fra/LC_MESSAGES/debug_kit.po (French)/app/Plugin/DebugKit/Locale/por/LC_MESSAGES/debug_kit.po (Portuguese)
The reason for that is that CakePHP will use the lower cased and underscored plugin name to compare it to the translation
domain and is going to look into the plugin if there is a match for the given translation file.
Controlling the Translation Order
The Configure value I18n.preferApp can be used to control the order of translations. If set to true in bootstrap it will
prefer the app translations over any plugins’ ones:
Configure::write('I18n.preferApp', true);
It defaults to false.
New in version 2.6.
Localization in CakePHP
To change or set the language for your application, all you need to do is the following:
Configure::write('Config.language', 'fra');This tells CakePHP which locale to use (if you use a regional locale, such as fr_FR, it will use the ISO 639-288 locale asa fallback if it doesn’t exist), you can change the language at any time during a request. e.g. in your bootstrap if you’resetting the application default language, in your (app) controller beforeFilter if it’s specific to the request or user, or infact anytime at all before you want a message in a different language. To set the language for the current user, you canstore the setting in the Session object, like this:$this->Session->write('Config.language', 'fra');At the beginning of each request in your controller’s beforeFilter you should configure Configure as well:class AppController extends Controller {public function beforeFilter() {if ($this->Session->check('Config.language')) {Configure::write('Config.language', $this->Session->read('Config.language'));}}}Doing this will ensure that both I18n and TranslateBehavior access the same language value.
It’s a good idea to serve up public content available in multiple languages from a unique URL - this makes it easy
for users (and search engines) to find what they’re looking for in the language they are expecting. There are several
ways to do this, it can be by using language specific subdomains (en.example.com, fra.example.com, etc.), or using a
88 https://www.loc.gov/standards/iso639-2/php/code_list.php

Utilities

461

CakePHP Cookbook Documentation, Release 2.x

prefix to the URL such as is done with this application. You may also wish to glean the information from the browser’suser-agent, among other things.
As mentioned in the previous section, displaying localized content is done using the __() convenience function, or
one of the other translation functions all of which are globally available, but probably be best utilized in your views.
The first parameter of the function is used as the msgid defined in the .po files.
CakePHP will automatically assume that all model validation error messages in your $validate array are intended to
be localized. When running the i18n shell these strings will also be extracted.
There’s one other aspect of localizing your application which is not covered by the use of the translate functions, andthat is date/money formats. Don’t forget that CakePHP is PHP :), therefore to set the formats for these things you need
to use setlocale89 .
If you pass a locale that doesn’t exist on your computer to setlocale90 it will have no effect. You can find the list ofavailable locales by running the command locale -a in a terminal.
Translating model validation errors
CakePHP will automatically extract the validation error when you are using the i18n console task. By default, thedefault domain is used. This can be overwritten by setting the $validationDomain property in your model:
class User extends AppModel {public $validationDomain = 'validation_errors';}
Additional parameters defined in the validation rule are passed to the translation function. This allows you to create
dynamic validation messages:
class User extends AppModel {public $validationDomain = 'validation';public $validate = array('username' => array('length' => array('rule' => array('between', 2, 10),'message' => 'Username should be between %d and %d characters')))}
Which will do the following internal call:__d('validation', 'Username should be between %d and %d characters', array(2, 10));89 https://www.php.net/setlocale
90 https://www.php.net/setlocale

462

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Logging
While CakePHP core Configure Class settings can really help you see what’s happening under the hood, there arecertain times that you’ll need to log data to the disk in order to find out what’s going on. In a world that is becoming
more dependent on technologies like SOAP and AJAX, debugging can be rather difficult.
Logging can also be a way to find out what’s been going on in your application over time. What search terms are being
used? What sorts of errors are my users being shown? How often is a particular query being executed?
Logging data in CakePHP is easy - the log() function is a part of the Object class, which is the common ancestor foralmost all CakePHP classes. If the context is a CakePHP class (Model, Controller, Component. . . almost anything),
you can log your data. You can also use CakeLog::write() directly. See Writing to logs
Creating and configuring log streams
Log stream handlers can be part of your application, or part of plugins. If for example you had a database logger
called DatabaseLog as part of your application, it would be placed in app/Lib/Log/Engine/DatabaseLog.php. If
you had a database logger as part of a plugin, it would be placed in app/Plugin/LoggingPack/Lib/Log/Engine/
DatabaseLog.php. When configured, CakeLog will attempt to load Configuring log streams, which is done by calling
CakeLog::config(). Configuring our DatabaseLog would look like:// for app/Lib
CakeLog::config('otherFile', array('engine' => 'Database','model' => 'LogEntry',// ...
));// for plugin called LoggingPack
CakeLog::config('otherFile', array('engine' => 'LoggingPack.Database','model' => 'LogEntry',// ...
));
When configuring a log stream the engine parameter is used to locate and load the log handler. All of the otherconfiguration properties are passed to the log stream’s constructor as an array.
App::uses('BaseLog', 'Log/Engine');class DatabaseLog extends BaseLog {public function __construct($options = array()) {parent::__construct($options);// ...
}
public function write($type, $message) {// write to the database.
}}
While CakePHP has no requirements for Log streams other than that they must implement a write method, extending
the BaseLog class has a few benefits:• It automatically handles the scope and type argument casting.
Utilities463

CakePHP Cookbook Documentation, Release 2.x• It implements the config() method which is required to make scoped logging work.
Each logger’s write method must take two parameters: $type, $message (in that order). $type is the string type ofthe logged message; core values are error, warning, info and debug. Additionally you can define your own types
by using them when you call CakeLog::write.
New in version 2.4.
As of 2.4 FileLog engine takes a few new options:• size Used to implement basic log file rotation. If log file size reaches the specified size, the existing file isrenamed by appending timestamp to filename and a new log file is created. Can be integer bytes value or humanreadable string values like ‘10MB’, ‘100KB’ etc. Defaults to 10MB. Setting size to false will disable the rotate
option below.
• rotate Log files are rotated a specified number of times before being removed. If the value is 0, old versions
are removed rather than rotated. Defaults to 10.
• mask Set the file permissions for created files. If left empty the default permissions are used.
Warning: Prior to 2.4 you had to include the suffix Log in your configuration (LoggingPack.DatabaseLog).
This is not necessary anymore. If you have been using a Log engine like `DatabaseLogger that does not follow
the convention to use a suffix Log for your class name, you have to adjust your class name to DatabaseLog. You
should also avoid class names like SomeLogLog, which includes the suffix twice at the end.

Note: Always configure loggers in app/Config/bootstrap.php Trying to use Application or plugin loggers incore.php will cause issues, as application paths are not yet configured.
Also new in 2.4: In debug mode missing directories will now be automatically created to avoid unnecessary errors
thrown when using the FileEngine.

Error and Exception logging
Errors and Exceptions can also be logged by configuring the corresponding values in your core.php file. Errors will be
displayed when debug > 0 and logged when debug == 0. Set Exception.log to true to log uncaught exceptions. See
Configuration for more information.
Interacting with log streams
You can introspect the configured streams with CakeLog::configured(). The return value of configured() is anarray of all the currently configured streams. You can remove streams using CakeLog::drop(). Once a log stream
has been dropped, it will no longer receive messages.

464

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Using the default FileLog class
While CakeLog can be configured to write to a number of user configured logging adapters, it also comes with adefault logging configuration. The default logging configuration will be used any time there are no other logging
adapters configured. Once a logging adapter has been configured, you will need to also configure FileLog if you wantfile logging to continue.
As its name implies, FileLog writes log messages to files. The type of log message being written determines the nameof the file the message is stored in. If a type is not supplied, LOG_ERROR is used, which writes to the error log. The
default log location is app/tmp/logs/$type.log:// Executing this inside a CakePHP class$this->log("Something didn't work!");// Results in this being appended to app/tmp/logs/error.log// 2007-11-02 10:22:02 Error: Something didn't work!
You can specify a custom log name using the first parameter. The default built-in FileLog class will treat this log nameas the file you wish to write logs to:// called statically
CakeLog::write('activity', 'A special message for activity logging');// Results in this being appended to app/tmp/logs/activity.log (rather than error.log)// 2007-11-02 10:22:02 Activity: A special message for activity logging
The configured directory must be writable by the web server user in order for logging to work correctly.
You can configure additional/alternate FileLog locations using CakeLog::config(). FileLog accepts a path whichallows for custom paths to be used:
CakeLog::config('custom_path', array('engine' => 'File','path' => '/path/to/custom/place/'));

Logging to Syslog
New in version 2.4.
In production environments it is highly recommended that you setup your system to use syslog instead of the files
logger. This will perform much better as all writes will be done in a (almost) non-blocking fashion. Your operating
system logger can be configured separately to rotate files, pre-process writes or use a completely different storage foryour logs.
Using syslog is pretty much like using the default FileLog engine; you just need to specify Syslog as the engine to be
used for logging. The following configuration snippet will replace the default logger with syslog. This should be donein the bootstrap.php file:
CakeLog::config('default', array('engine' => 'Syslog'));
The configuration array accepted for the Syslog logging engine understands the following keys:

Utilities465

CakePHP Cookbook Documentation, Release 2.x• format: A sprintf template string with two placeholders; the first one for the error type, and the second for themessage itself. This key is useful to add additional information about the server or process in the logged message.
For example: %s - Web Server 1 - %s will look like error - Web Server 1 - An error occurredin this request after replacing the placeholders.
• prefix: An string that will be prefixed to every logged message.
• flag: An integer flag to be used for opening the connection to the logger. By default LOG_ODELAY will be used.
See openlog documentation for more options• facility: The logging slot to use in syslog. By default LOG_USER is used. See syslog documentation for more
options
Writing to logs
Writing to the log files can be done in 2 different ways. The first is to use the static CakeLog::write() method:
CakeLog::write('debug', 'Something did not work');
The second is to use the log() shortcut function available on any class that extends Object. Calling log() will internallycall CakeLog::write():// Executing this inside a CakePHP class:$this->log("Something did not work!", 'debug');
All configured log streams are sequentially written to each time CakeLog::write() is called.
Changed in version 2.5.
CakeLog does not auto-configure itself anymore. As a result, log files will not be auto-created anymore if no stream islistening. Make sure you have at least one default stream set up if you want to listen to all types and levels. Usually,
you can just set the core FileLog class to output into app/tmp/logs/:
CakeLog::config('default', array('engine' => 'File'));

Logging Scopes
New in version 2.2.
Often times you’ll want to configure different logging behavior for different subsystems or parts of your application.
Take for example an e-commerce shop; You’ll probably want to handle logging for orders and payments differently
than you do other less critical logs.
CakePHP exposes this concept as logging scopes. When log messages are written you can include a scope name. If
there is a configured logger for that scope, the log messages will be directed to those loggers. If a log message is written
to an unknown scope, loggers that handle that level of message will log the message. For example:// Configure tmp/logs/shop.log to receive the two configured types (log levels), but only// those with `orders` and `payments` as scope
CakeLog::config('shop', array('engine' => 'FileLog','types' => array('warning', 'error'),'scopes' => array('orders', 'payments'),(continues on next page)

466

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'file' => 'shop.log',));// Configure tmp/logs/payments.log to receive the two configured types, but only// those with `payments` as scope
CakeLog::config('payments', array('engine' => 'SyslogLog','types' => array('info', 'error', 'warning'),'scopes' => array('payments')));
CakeLog::warning('This gets written only to shops stream', 'orders');
CakeLog::warning('This gets written to both shops and payments streams', 'payments');
CakeLog::warning('This gets written to both shops and payments streams', 'unknown');
In order for scopes to work, you must do a few things:
1. Define the accepted types on loggers that use scopes.
2. Loggers using scopes must implement a config() method. Extending the BaseLog class is the easiest way to
get a compatible method.
CakeLog APIclass CakeLog
A simple class for writing to logs.
static CakeLog::config($name, $config)
Parameters• $name (string) – Name for the logger being connected, used to drop a logger later on.
• $config (array) – Array of configuration information and constructor arguments for thelogger.
Connect a new logger to CakeLog. Each connected logger receives all log messages each time a log message iswritten.
static CakeLog::configured
Returns
An array of configured loggers.
Get the names of the configured loggers.
static CakeLog::drop($name)Parameters• $name (string) – Name of the logger you wish to no longer receive messages.
static CakeLog::write($level, $message, $scope = array())
Write a message into all the configured loggers. $level indicates the level of log message being created. $message
is the message of the log entry being written to.
Changed in version 2.2: $scope was added
New in version 2.2: Log levels and scopes
Utilities467

CakePHP Cookbook Documentation, Release 2.x

static CakeLog::levels
Call this method without arguments, eg: CakeLog::levels() to obtain current level configuration.
To append the additional levels ‘user0’ and ‘user1’ to the default log levels use:
CakeLog::levels(array('user0', 'user1'));// or
CakeLog::levels(array('user0', 'user1'), true);
Calling CakeLog::levels() will result in:array(
0 => 'emergency',
1 => 'alert',// ...
8 => 'user0',
9 => 'user1',);
To set/replace an existing configuration, pass an array with the second argument set to false:
CakeLog::levels(array('user0', 'user1'), false);
Calling CakeLog::levels() will result in:array(
0 => 'user0',
1 => 'user1',);
static CakeLog::defaultLevels
Returns
An array of the default log levels values.
Resets log levels to their original values:
array('emergency' => LOG_EMERG,'alert'=> LOG_ALERT,'critical' => LOG_CRIT,'error'=> LOG_ERR,'warning'=> LOG_WARNING,'notice'=> LOG_NOTICE,'info'=> LOG_INFO,'debug'=> LOG_DEBUG,);
static CakeLog::enabled($streamName)
ReturnsbooleanChecks whether $streamName has been enabled.
static CakeLog::enable($streamName)

468

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Returnsvoid
Enable the stream $streamName.
static CakeLog::disable($streamName)
Returnsvoid
Disable the stream $streamName.
static CakeLog::stream($streamName)
Returns
Instance of BaseLog or false if not found.
Gets $streamName from the active streams.
Convenience methods
New in version 2.2.
The following convenience methods were added to log $message with the appropriate log level.
static CakeLog::emergency($message, $scope = array())
static CakeLog::alert($message, $scope = array())
static CakeLog::critical($message, $scope = array())
static CakeLog::error($message, $scope = array())
static CakeLog::warning($message, $scope = array())
static CakeLog::notice($message, $scope = array())
static CakeLog::info($message, $scope = array())
static CakeLog::debug($message, $scope = array())
CakeNumber
class CakeNumber
If you need NumberHelper functionalities outside of a View, use the CakeNumber class:class UsersController extends AppController {public $components = array('Auth');public function afterLogin() {App::uses('CakeNumber', 'Utility');$storageUsed = $this->Auth->user('storage_used');if ($storageUsed > 5000000) {// notify users of quota$this->Session->setFlash(__('You are using %s storage',␣
(continues on next page)

Utilities469

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

CakeNumber::toReadableSize($storageUsed)));
}
}

˓→

}
New in version 2.1: CakeNumber has been factored out from NumberHelper.
All of these functions return the formatted number; They do not automatically echo the output into the view.
CakeNumber::currency(float $number, string $currency = 'USD', array $options = array())
Parameters• $number (float) – The value to covert.
• $currency (string) – The known currency format to use.
• $options (array) – Options, see below.
This method is used to display a number in common currency formats (EUR,GBP,USD). Usage in a view looks
like:// called as NumberHelper
echo $this->Number->currency($number, $currency);// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::currency($number, $currency);
The first parameter, $number, should be a floating point number that represents the amount of money you are
expressing. The second parameter is used to choose a predefined currency formatting scheme:$currency

1234.56, formatted by currency type

EUR
GBP
USD

€1.234,56£1,234.56$1,234.56

The third parameter is an array of options for further defining the output. The following options are available:

470

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

OptionDescription

beforeafter

The currency symbol to place before whole numbers ie. ‘$’
The currency symbol to place after decimal numbers ie. ‘c’. Set to boolean false to use nodecimal symbol. eg. 0.35 => $0.35.
The text to use for zero values, can be a string or a number. ie. 0, ‘Free!’
Number of decimal places to use. ie. 2
Thousands separator ie. ‘,’
Decimal separator symbol ie. ‘.’
Symbol for negative numbers. If equal to ‘()’, the number will be wrapped with ( and )Should the output be htmlentity escaped? Defaults to true
String to use for whole numbers ie. ‘ dollars’

zeroplacesthousands
decimals
negative
escapewholeSymbol
wholePosition
fractionSymbol
fractionPosition
fractionExponent

Either ‘before’ or ‘after’ to place the whole symbol
String to use for fraction numbers ie. ‘ cents’
Either ‘before’ or ‘after’ to place the fraction symbol
Fraction exponent of this specific currency. Defaults to 2.

If a non-recognized $currency value is supplied, it is prepended to a USD formatted number. For example:// called as NumberHelper
echo $this->Number->currency('1234.56', 'FOO');// Outputs
FOO 1,234.56// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::currency('1234.56', 'FOO');
Changed in version 2.4: The fractionExponent option was added.
CakeNumber::defaultCurrency(string $currency)
Parameters• $currency (string) – Set a known currency for CakeNumber::currency().
Setter/getter for default currency.
This removes the need always passing the currency to
CakeNumber::currency() and change all currency outputs by setting other default.
New in version 2.3: This method was added in 2.3
CakeNumber::addFormat(string $formatName, array $options)Parameters• $formatName (string) – The format name to be used in the future• $options (array) – The array of options for this format. Uses the same $options keys as
CakeNumber::currency().
Add a currency format to the Number helper. Makes reusing currency formats easier:Utilities

471

CakePHP Cookbook Documentation, Release 2.x// called as NumberHelper$this->Number->addFormat('BRL', array('before' => 'R$', 'thousands' => '.',
˓→'decimals' => ','));// called as CakeNumber
App::uses('CakeNumber', 'Utility');
CakeNumber::addFormat('BRL', array('before' => 'R$', 'thousands' => '.', 'decimals'␣
˓→=> ','));
You can now use BRL as a short form when formatting currency amounts:// called as NumberHelper
echo $this->Number->currency($value, 'BRL');// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::currency($value, 'BRL');
Added formats are merged with the following defaults:
array('wholeSymbol'=> '','wholePosition'=> 'before','fractionSymbol'=> false,'fractionPosition' => 'after','zero'=> 0,'places'=> 2,'thousands'=> ',','decimals'=> '.','negative'=> '()','escape'=> true,'fractionExponent' => 2)
CakeNumber::precision(mixed $number, int $precision = 3)
Parameters• $number (float) – The value to covert• $precision (integer) – The number of decimal places to display
This method displays a number with the specified amount of precision (decimal places). It will round in order tomaintain the level of precision defined.
// called as NumberHelper
echo $this->Number->precision(456.91873645, 2);// Outputs
456.92// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::precision(456.91873645, 2);472

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

CakeNumber::toPercentage(mixed $number, int $precision = 2, array $options = array())
Parameters• $number (float) – The value to covert.
• $precision (integer) – The number of decimal places to display.
• $options (array) – Options, see below.
OptionDescription

multiply

Boolean to indicate whether the value has to be multiplied by 100. Useful for decimal percentages.

Like precision(), this method formats a number according to the supplied precision (where numbers are rounded
to meet the given precision). This method also expresses the number as a percentage and prepends the output
with a percent sign.
// Called as NumberHelper. Output: 45.69%
echo $this->Number->toPercentage(45.691873645);// Called as CakeNumber. Output: 45.69%
App::uses('CakeNumber', 'Utility');echo CakeNumber::toPercentage(45.691873645);// Called with multiply. Output: 45.69%
echo CakeNumber::toPercentage(0.45691, 2, array('multiply' => true));
New in version 2.4: The $options argument with the multiply option was added.
CakeNumber::fromReadableSize(string $size, $default)
Parameters• $size (string) – The formatted human readable value.
This method unformats a number from a human readable byte size to an integer number of bytes.
New in version 2.3: This method was added in 2.3
CakeNumber::toReadableSize(string $dataSize)Parameters• $dataSize (string) – The number of bytes to make readable.
This method formats data sizes in human readable forms. It provides a shortcut way to convert bytes to KB, MB,
GB, and TB. The size is displayed with a two-digit precision level, according to the size of data supplied (i.e.
higher sizes are expressed in larger terms):// called as NumberHelper
echo $this->Number->toReadableSize(0); // 0 Bytes
echo $this->Number->toReadableSize(1024); // 1 KB
echo $this->Number->toReadableSize(1321205.76); // 1.26 MB
echo $this->Number->toReadableSize(5368709120); // 5.00 GB(continues on next page)

Utilities473

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::toReadableSize(0); // 0 Bytes
echo CakeNumber::toReadableSize(1024); // 1 KB
echo CakeNumber::toReadableSize(1321205.76); // 1.26 MB
echo CakeNumber::toReadableSize(5368709120); // 5.00 GB
CakeNumber::format(mixed $number, mixed $options=false)
This method gives you much more control over the formatting of numbers for use in your views (and is used as
the main method by most of the other NumberHelper methods). Using this method might looks like:// called as NumberHelper$this->Number->format($number, $options);// called as CakeNumber
CakeNumber::format($number, $options);
The $number parameter is the number that you are planning on formatting for output. With no $options supplied,the number 1236.334 would output as 1,236. Note that the default precision is zero decimal places.
The $options parameter is where the real magic for this method resides.
• If you pass an integer then this becomes the amount of precision or places for the function.
• If you pass an associated array, you can use the following keys:– places (integer): the amount of desired precision– before (string): to be put before the outputted number– escape (boolean): if you want the value in before to be escaped– decimals (string): used to delimit the decimal places in a number– thousands (string): used to mark off thousand, millions, . . . places
Example:// called as NumberHelper
echo $this->Number->format('123456.7890', array('places' => 2,'before' => '¥ ','escape' => false,'decimals' => '.',
'thousands' => ','));// output '¥ 123,456.79'// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::format('123456.7890', array('places' => 2,'before' => '¥ ','escape' => false,'decimals' => '.',
'thousands' => ','(continues on next page)474

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)));// output '¥ 123,456.79'
CakeNumber::formatDelta(mixed $number, mixed $options=array())
This method displays differences in value as a signed number:// called as NumberHelper$this->Number->formatDelta($number, $options);// called as CakeNumber
CakeNumber::formatDelta($number, $options);
The $number parameter is the number that you are planning on formatting for output. With no $options supplied,the number 1236.334 would output as 1,236. Note that the default precision is zero decimal places.
The $options parameter takes the same keys as CakeNumber::format() itself:• places (integer): the amount of desired precision• before (string): to be put before the outputted number• after (string): to be put after the outputted number• decimals (string): used to delimit the decimal places in a number• thousands (string): used to mark off thousand, millions, . . . places
Example:// called as NumberHelper
echo $this->Number->formatDelta('123456.7890', array('places' => 2,'decimals' => '.',
'thousands' => ','));// output '+123,456.79'// called as CakeNumber
App::uses('CakeNumber', 'Utility');echo CakeNumber::formatDelta('123456.7890', array('places' => 2,'decimals' => '.',
'thousands' => ','));// output '+123,456.79'
New in version 2.3: This method was added in 2.3

Utilities475

CakePHP Cookbook Documentation, Release 2.x

RouterRouter can be used to parse URLs into arrays containing indexes for the controller, action, and any parameters, andthe opposite: to convert URL arrays (eg. array('controller' => 'posts', 'action' => 'index')) to string
URLs.
Read more about ways to configure the Router and the Router class.
Data Sanitization
The Sanitize class is deprecated as of 2.4, and will be removed in CakePHP 3.0. Instead of using the Sanitize classyou can accomplish the same tasks using other parts of CakePHP, native PHP functions, or other libraries.
Input filtering
Instead of using the destructive input filtering features of Sanitize class you should instead apply more thorough Data
Validation to the user data your application accepts. By rejecting invalid input you can often remove the need todestructively modify user data. You might also want to look at PHP’s filter extension91 in situations you need tomodify user input.
Accepting user submitted HTML
Often input filtering is used when accepting user-submitted HTML. In these situations it is best to use a dedicatedlibrary like HTML Purifier92 .
SQL Escaping
CakePHP handles SQL escaping on associated array values provided to Model::find() and Model::save(). In therare case you need to construct SQL by hand using user input you should use Prepared Statements.
Securityclass Security
The security library93 handles basic security measures such as providing methods for hashing and encrypting data.
Note: By default the encryption features offered by Security rely on the deprecated mcrypt extension. This behaviour
can be changed by setting Security.useOpenSsl. If you are using the default behaviour using PHP>=7.1 you willneed to install mcrypt via PECL.
91 https://www.php.net/filter
92 https://htmlpurifier.org/

93 https://api.cakephp.org/2.x/class-Security.html

476

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Security API
static Security::cipher($text, $key)
Return typestringEncrypts/Decrypts text using the given key:// Encrypt your text with my_key$secret = Security::cipher('hello world', 'my_key');// Later decrypt your text
$nosecret = Security::cipher($secret, 'my_key');

Warning: cipher() uses a weak XOR cipher and should not be used. It is only included for backwards
compatibility.
static Security::rijndael($text, $key, $mode)
Parameters• $text (string) – The text to encrypt• $key (string) – The key to use for encryption. This must be longer than 32 bytes.
• $mode (string) – The mode to use, either ‘encrypt’ or ‘decrypt’
Encrypts/Decrypts text using the rijndael-256 cipher. This requires the mcrypt extension94 to be installed:// Encrypt some data.
$encrypted = Security::rijndael('a secret', Configure::read('Security.key'),
˓→'encrypt');// Later decrypt it.
$decrypted = Security::rijndael($encrypted, Configure::read('Security.key'),
˓→'decrypt');
rijndael() can be used to store data you need to decrypt later, like the contents of cookies. It should never beused to store passwords. Instead you should use the one way hashing methods provided by hash()
New in version 2.2: Security::rijndael() was added in 2.2.
static Security::encrypt($text, $key, $hmacSalt = null)Parameters• $plain (string) – The value to encrypt.
• $key (string) – The 256 bit/32 byte key to use as a cipher key.
• $hmacSalt (string) – The salt to use for the HMAC process. Leave null to use Security.salt.
Encrypt $text using AES-256. The $key should be a value with a lots of variance in the data, much like a good
password. The returned result will be the encrypted value with an HMAC checksum.
This method should never be used to store passwords. Instead you should use the one way hashing methods
provided by hash(). An example use would be:
94 https://www.php.net/mcrypt

Utilities477

CakePHP Cookbook Documentation, Release 2.x// Assuming key is stored somewhere it can be re-used for// decryption later.
$key = 'wt1U5MACWJFTXGenFoZoiLwQGrLgdbHA';$result = Security::encrypt($value, $key);
Encrypted values can be decrypted using Security::decrypt().
New in version 2.5.
New in version 2.10.8: Set Security.useOpenSsl to use OpenSSL instead of the deprecated mcrypt extension.
static Security::decrypt($cipher, $key, $hmacSalt = null)Parameters• $cipher (string) – The ciphertext to decrypt.
• $key (string) – The 256 bit/32 byte key to use as a cipher key.
• $hmacSalt (string) – The salt to use for the HMAC process. Leave null to use Security.salt.
Decrypt a previously encrypted value. The $key and $hmacSalt parameters must match the values used toencrypt or decryption will fail. An example use would be:// Assuming key is stored somewhere it can be re-used for// decryption later.
$key = 'wt1U5MACWJFTXGenFoZoiLwQGrLgdbHA';$cipher = $user['User']['secrets'];$result = Security::decrypt($cipher, $key);
If the value cannot be decrypted due to changes in the key or HMAC salt false will be returned.
New in version 2.5.
New in version 2.10.8: Set Security.useOpenSsl to use OpenSSL instead of the deprecated mcrypt extension.
static Security::hash($string, $type = NULL, $salt = false)
Return typestringCreate a hash from a string using given method or fallback to next available method. If $salt is set to true, theapplications salt value will be used:// Using the application's salt value$sha1 = Security::hash('CakePHP Framework', 'sha1', true);// Using a custom salt value$md5 = Security::hash('CakePHP Framework', 'md5', 'my-salt');// Using the default hash algorithm$hash = Security::hash('CakePHP Framework');hash() also supports other secure hashing algorithms like bcrypt. When using bcrypt, you should be mindfulof the slightly different usage. Creating an initial hash works the same as other algorithms:// Create a hash using bcrypt
Security::setHash('blowfish');$hash = Security::hash('CakePHP Framework');

478

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Unlike other hash types comparing plain text values to hashed values should be done as follows:// $storedPassword, is a previously generated bcrypt hash.
$newHash = Security::hash($newPassword, 'blowfish', $storedPassword);
When comparing values hashed with bcrypt, the original hash should be provided as the $salt parameter. This
allows bcrypt to reuse the same cost and salt values, allowing the generated hash to return the same resulting
hash, given the same input value.
Changed in version 2.3: Support for bcrypt was added in 2.3
static Security::setHash($hash)
Return type
void
Sets the default hash method for the Security object. This affects all objects using Security::hash().
Set
class Set
Array management, if done right, can be a very powerful and useful tool for building smarter, more optimized code.
CakePHP offers a very useful set of static utilities in the Set class that allow you to do just that.
CakePHP’s Set class can be called from any model or controller in the same way Inflector is called. Example:
Set::combine().
Deprecated since version 2.2: The Set class has been deprecated in 2.2 in favour of the Hash class. It offers a moreconsistent interface and API.
Set-compatible Path syntax
The Path syntax is used by (for example) sort, and is used to define a path.
Usage example (using Set::sort()):$a = array(0 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' =>˓→'Nate'))),
1 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' =>˓→'Lindsay'))),
2 => array('Person' => array('name' => 'Adam'),'Friend' => array(array('name' => 'Bob˓→'))));$result = Set::sort($a, '{n}.Person.name', 'asc');/* result now looks likearray(
0 => array('Person' => array('name' => 'Adam'),'Friend' => array(array('name' => 'Bob'))),
1 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate'))),
2 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' => 'Lindsay˓→'))));*/

Utilities479

CakePHP Cookbook Documentation, Release 2.x

As you can see in the example above, some things are wrapped in {}’s, others not. In the table below, you can see
which options are available.
Expression

Definition

{n}
{s}
Foo
{[a-z]+}

Represents a numeric key
Represents a string
Any string (without enclosing brackets) is treated like a string literal.
Any string enclosed in brackets (besides {n} and {s}) is interpreted as a regular expression.

static Set::apply($path, $array, $callback, $options = array())
Return type
mixed
Apply a callback to the elements of an array extracted by a Set::extract compatible path:$data = array(
array('Movie' => array('id' => 1, 'title' => 'movie 3', 'rating' => 5)),array('Movie' => array('id' => 1, 'title' => 'movie 1', 'rating' => 1)),array('Movie' => array('id' => 1, 'title' => 'movie 2', 'rating' => 3)),);
$result = Set::apply('/Movie/rating', $data, 'array_sum');// result equals 9$result = Set::apply('/Movie/title', $data, 'strtoupper', array('type' => 'map'));// result equals array('MOVIE 3', 'MOVIE 1', 'MOVIE 2')// $options are: - type : can be 'pass' uses call_user_func_array(), 'map' uses array_
˓→map(), or 'reduce' uses array_reduce()
static Set::check($data, $path = null)
Return typeboolean/array
Checks if a particular path is set in an array. If $path is empty, $data will be returned instead of a boolean value:$set = array('My Index 1' => array('First' => 'The first item'));$result = Set::check($set, 'My Index 1.First');// $result == True
$result = Set::check($set, 'My Index 1');// $result == True
$result = Set::check($set, array());// $result == array('My Index 1' => array('First' => 'The first item'))$set = array('My Index 1' => array('First' =>array('Second' =>array('Third' =>array('Fourth' => 'Heavy. Nesting.')))));$result = Set::check($set, 'My Index 1.First.Second');// $result == True(continues on next page)

480

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$result = Set::check($set, 'My Index 1.First.Second.Third');// $result == True
$result = Set::check($set, 'My Index 1.First.Second.Third.Fourth');// $result == True
$result = Set::check($set, 'My Index 1.First.Seconds.Third.Fourth');// $result == False
static Set::classicExtract($data, $path = null)
Return typemixed
Gets a value from an array or object that is contained in a given path using an array path syntax, i.e.:• “{n}.Person.{[a-z]+}” - Where “{n}” represents a numeric key, “Person” represents a string literal• “{[a-z]+}” (i.e. any string literal enclosed in brackets besides {n} and {s}) is interpreted as a regularexpression.
Example 1$a = array(
array('Article' => array('id' => 1, 'title' => 'Article 1')),array('Article' => array('id' => 2, 'title' => 'Article 2')),array('Article' => array('id' => 3, 'title' => 'Article 3')));
$result = Set::classicExtract($a, '{n}.Article.id');/* $result now looks like:Array([0] => 1[1] => 2[2] => 3)*/$result = Set::classicExtract($a, '{n}.Article.title');/* $result now looks like:Array([0] => Article 1[1] => Article 2[2] => Article 3)*/$result = Set::classicExtract($a, '1.Article.title');// $result == "Article 2"$result = Set::classicExtract($a, '3.Article.title');// $result == null
Example 2$a = array(0 => array('pages' => array('name' => 'page')),1 => array('fruites' => array('name' => 'fruit')),Utilities(continues on next page)481

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'test' => array(array('name' => 'jippi')),'dot.test' => array(array('name' => 'jippi')));$result = Set::classicExtract($a, '{n}.{s}.name');/* $result now looks like:Array([0] => Array([0] => page)[1] => Array([0] => fruit))*/$result = Set::classicExtract($a, '{s}.{n}.name');/* $result now looks like:Array([0] => Array([0] => jippi)[1] => Array([0] => jippi))*/$result = Set::classicExtract($a,'{\w+}.{\w+}.name');/* $result now looks like:Array([0] => Array([pages] => page)[1] => Array([fruites] => fruit)[test] => Array([0] => jippi)[dot.test] => Array([0] => jippi)(continues on next page)482

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page))*/$result = Set::classicExtract($a,'{\d+}.{\w+}.name');/* $result now looks like:Array([0] => Array([pages] => page)[1] => Array([fruites] => fruit))*/$result = Set::classicExtract($a,'{n}.{\w+}.name');/* $result now looks like:Array([0] => Array([pages] => page)[1] => Array([fruites] => fruit))*/$result = Set::classicExtract($a,'{s}.{\d+}.name');/* $result now looks like:Array([0] => Array([0] => jippi)[1] => Array([0] => jippi))*/$result = Set::classicExtract($a,'{s}');/* $result now looks like:Array([0] => Array([0] => Array(continues on next page)

Utilities483

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)([name] => jippi))[1] => Array([0] => Array([name] => jippi)))*/$result = Set::classicExtract($a,'{[a-z]}');/* $result now looks like:Array([test] => Array([0] => Array([name] => jippi))[dot.test] => Array([0] => Array([name] => jippi)))*/$result = Set::classicExtract($a, '{dot\.test}.{n}');/* $result now looks like:Array([dot.test] => Array([0] => Array([name] => jippi)))*/
static Set::combine($data, $path1 = null, $path2 = null, $groupPath = null)
Return type
array

484

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Creates an associative array using a $path1 as the path to build its keys, and optionally $path2 as path to get thevalues. If $path2 is not specified, all values will be initialized to null (useful for Set::merge). You can optionallygroup the values by what is obtained when following the path specified in $groupPath.
$result = Set::combine(array(), '{n}.User.id', '{n}.User.Data');// $result == array();$result = Set::combine('', '{n}.User.id', '{n}.User.Data');// $result == array();$a = array(
array('User' => array('id' => 2,'group_id' => 1,'Data' => array('user' => 'mariano.iglesias','name' => 'Mariano Iglesias'))),
array('User' => array('id' => 14,'group_id' => 2,'Data' => array('user' => 'phpnut','name' => 'Larry E. Masters'))),array('User' => array('id' => 25,'group_id' => 1,'Data' => array('user' => 'gwoo','name' => 'The Gwoo'))));$result = Set::combine($a, '{n}.User.id');/* $result now looks like:Array([2] =>[14] =>[25] =>)*/$result = Set::combine($a, '{n}.User.id', '{n}.User.non-existent');(continues on next page)

Utilities

485

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)/* $result now looks like:Array([2] =>[14] =>[25] =>)*/$result = Set::combine($a, '{n}.User.id', '{n}.User.Data');/* $result now looks like:Array([2] => Array([user] => mariano.iglesias[name] => Mariano Iglesias)[14] => Array([user] => phpnut[name] => Larry E. Masters)[25] => Array([user] => gwoo
[name] => The Gwoo))*/
$result = Set::combine($a, '{n}.User.id', '{n}.User.Data.name');/* $result now looks like:Array([2] => Mariano Iglesias[14] => Larry E. Masters[25] => The Gwoo)*/
$result = Set::combine($a, '{n}.User.id', '{n}.User.Data', '{n}.User.group_id');/* $result now looks like:Array([1] => Array([2] => Array([user] => mariano.iglesias[name] => Mariano Iglesias)(continues on next page)

486

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[25] => Array([user] => gwoo
[name] => The Gwoo))[2] => Array([14] => Array([user] => phpnut[name] => Larry E. Masters)))*/$result = Set::combine($a, '{n}.User.id', '{n}.User.Data.name', '{n}.User.group_id
˓→');/* $result now looks like:Array([1] => Array([2] => Mariano Iglesias[25] => The Gwoo)[2] => Array([14] => Larry E. Masters))*/$result = Set::combine($a, '{n}.User.id', array('{0}: {1}', '{n}.User.Data.user', '˓→{n}.User.Data.name'), '{n}.User.group_id');/* $result now looks like:Array([1] => Array([2] => mariano.iglesias: Mariano Iglesias[25] => gwoo: The Gwoo)[2] => Array([14] => phpnut: Larry E. Masters))*/$result = Set::combine($a, array('{0}: {1}', '{n}.User.Data.user', '{n}.User.Data.
(continues on next page)

Utilities487

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)name'), '{n}.User.id');/* $result now looks like:Array([mariano.iglesias: Mariano Iglesias] => 2
[phpnut: Larry E. Masters] => 14[gwoo: The Gwoo] => 25)*/
˓→$result = Set::combine($a, array('{1}: {0}', '{n}.User.Data.user', '{n}.User.Data.
˓→name'), '{n}.User.id');/* $result now looks like:
Array([Mariano Iglesias: mariano.iglesias] => 2
[Larry E. Masters: phpnut] => 14[The Gwoo: gwoo] => 25)*/$result = Set::combine($a, array('%1$s: %2$d', '{n}.User.Data.user', '{n}.User.id'),˓→ '{n}.User.Data.name');/* $result now looks like:Array([mariano.iglesias: 2] => Mariano Iglesias[phpnut: 14] => Larry E. Masters[gwoo: 25] => The Gwoo)*/
$result = Set::combine($a, array('%2$d: %1$s', '{n}.User.Data.user', '{n}.User.id'),˓→ '{n}.User.Data.name');/* $result now looks like:Array([2: mariano.iglesias] => Mariano Iglesias[14: phpnut] => Larry E. Masters[25: gwoo] => The Gwoo)*/
static Set::contains($val1, $val2 = null)
Return typebooleanDetermines if one Set or array contains the exact keys and values of another:$a = array(0 => array('name' => 'main'),(continues on next page)

488

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

1 => array('name' => 'about'));$b = array(0 => array('name' => 'main'),
1 => array('name' => 'about'),
2 => array('name' => 'contact'),'a' => 'b');$result = Set::contains($a, $a);// True$result = Set::contains($a, $b);// False$result = Set::contains($b, $a);// Truestatic Set::countDim($array = null, $all = false, $count = 0)
Return type
integerCounts the dimensions of an array. If $all is set to false (which is the default) it will only consider the dimensionof the first element in the array:$data = array('one', '2', 'three');$result = Set::countDim($data);// $result == 1
$data = array('1' => '1.1', '2', '3');$result = Set::countDim($data);// $result == 1
$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => '3.1.1'));$result = Set::countDim($data);// $result == 2$data = array('1' => '1.1', '2', '3' => array('3.1' => '3.1.1'));$result = Set::countDim($data);// $result == 1
$data = array('1' => '1.1', '2', '3' => array('3.1' => '3.1.1'));$result = Set::countDim($data, true);// $result == 2$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => array('3.1.
˓→1' => '3.1.1.1')));$result = Set::countDim($data);// $result == 2$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => array('3.1.
˓→1' => '3.1.1.1')));$result = Set::countDim($data, true);// $result == 3
(continues on next page)

Utilities489

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$data = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.
˓→1.1' => '2.1.1.1'))), '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));$result = Set::countDim($data, true);// $result == 4
$data = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.
˓→1.1' => array('2.1.1.1')))), '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));$result = Set::countDim($data, true);// $result == 5$data = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.
˓→1.1' => array('2.1.1.1' => '2.1.1.1.1')))), '3' => array('3.1' => array('3.1.1' =>˓→ '3.1.1.1')));$result = Set::countDim($data, true);// $result == 5$set = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.1.
˓→1' => array('2.1.1.1' => '2.1.1.1.1')))), '3' => array('3.1' => array('3.1.1' =>˓→'3.1.1.1')));$result = Set::countDim($set, false, 0);// $result == 2
$result = Set::countDim($set, true);// $result == 5
static Set::diff($val1, $val2 = null)
Return typearray
Computes the difference between a Set and an array, two Sets, or two arrays:$a = array(0 => array('name' => 'main'),
1 => array('name' => 'about'));$b = array(0 => array('name' => 'main'),
1 => array('name' => 'about'),
2 => array('name' => 'contact'));$result = Set::diff($a, $b);/* $result now looks like:Array([2] => Array([name] => contact))*/(continues on next page)

490

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$result = Set::diff($a, array());/* $result now looks like:Array([0] => Array([name] => main)[1] => Array([name] => about))*/$result = Set::diff(array(), $b);/* $result now looks like:Array([0] => Array([name] => main)[1] => Array([name] => about)[2] => Array([name] => contact))*/$b = array(0 => array('name' => 'me'),1 => array('name' => 'about'));$result = Set::diff($a, $b);/* $result now looks like:Array([0] => Array([name] => main))*/
static Set::enum($select, $list = null)
Return typestringUtilities

491

CakePHP Cookbook Documentation, Release 2.x

The enum method works well when using HTML select elements. It returns a value from an array list if the key
exists.
If a comma separated $list is passed arrays are numeric with the key of the first being 0 $list = ‘no, yes’ would
translate to $list = array(0 => ‘no’, 1 => ‘yes’);If an array is used, keys can be strings example: array(‘no’ => 0, ‘yes’ => 1);$list defaults to 0 = no 1 = yes if param is not passed:$res = Set::enum(1, 'one, two');// $res is 'two'$res = Set::enum('no', array('no' => 0, 'yes' => 1));// $res is 0$res = Set::enum('first', array('first' => 'one', 'second' => 'two'));// $res is 'one'
static Set::extract($path, $data = null, $options = array())
Return type
mixed
Set::extract uses basic XPath 2.0 syntax to return subsets of your data from a find or a find all. This function
allows you to retrieve your data quickly without having to loop through multi dimensional arrays or traverse
through tree structures.
Note: If $path does not contain a ‘/’ the call will be delegated to Set::classicExtract()// Common Usage:$users = $this->User->find("all");$results = Set::extract('/User/id', $users);// results returns:// array(1,2,3,4,5,...);
Currently implemented selectors:
Selector

Note/User/id
Similar to the classic {n}.User.id/User[2]/name
Selects the name of the second User
/User[id<2]
Selects all Users with an id < 2/User[id>2][<5]Selects all Users with an id > 2 but 5/Post/Comment[author_name=john]/../name
Selects the name of all Posts that have at least one Comment written by john
/Posts[title]
Selects all Posts that have a ‘title’ key/Comment/.[1]Selects the contents of the first comment/Comment/.[:last]
Selects the last comment/Comment/.[:first]
Selects the first comment/ComSelects all comments that have a text matching the regex /cakephp/i
ment[text=/cakephp/i]/Comment/@*
Selects the key names of all comments Currently only absolute paths starting with asingle ‘/’ are supported. Please report any bugs as you find them. Suggestions for
additional features are welcome.

492

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

To learn more about Set::extract() refer to the function testExtract() in /lib/Cake/Test/Case/Utility/
SetTest.php.
static Set::filter($var)
Return typearray
Filters empty elements out of a route array, excluding ‘0’:$res = Set::filter(array('0', false, true, 0, array('one thing', 'I can tell you',˓→'is you got to be', false)));/* $res now looks like:
Array (
[0] => 0[2] => 1[3] => 0[4] => Array([0] => one thing[1] => I can tell you[2] => is you got to be))*/static Set::flatten($data, $separator = '.')
Return typearray
Collapses a multi-dimensional array into a single dimension:$arr = array(
array('Post' => array('id' => '1', 'title' => 'First Post'),'Author' => array('id' => '1', 'user' => 'Kyle'),),array('Post' => array('id' => '2', 'title' => 'Second Post'),'Author' => array('id' => '3', 'user' => 'Crystal'),),);$res = Set::flatten($arr);/* $res now looks like:
Array (
[0.Post.id] => 1
[0.Post.title] => First Post
[0.Author.id] => 1
[0.Author.user] => Kyle
[1.Post.id] => 2
[1.Post.title] => Second Post
[1.Author.id] => 3
[1.Author.user] => Crystal(continues on next page)

Utilities493

CakePHP Cookbook Documentation, Release 2.x(continued from previous page))*/
static Set::format($data, $format, $keys)
Return typearray
Returns a series of values extracted from an array, formatted in a format string:$data = array(
array('Person' => array('first_name' => 'Nate', 'last_name' => 'Abele', 'city'␣
˓→=> 'Boston', 'state' => 'MA', 'something' => '42')),array('Person' => array('first_name' => 'Larry', 'last_name' => 'Masters', 'city
˓→' => 'Boondock', 'state' => 'TN', 'something' => '{0}')),
array('Person' => array('first_name' => 'Garrett', 'last_name' => 'Woodworth',
˓→'city' => 'Venice Beach', 'state' => 'CA', 'something' => '{1}')));$res = Set::format($data, '{1}, {0}', array('{n}.Person.first_name', '{n}.Person.
˓→last_name'));
/*
Array
(
[0] => Abele, Nate[1] => Masters, Larry[2] => Woodworth, Garrett)*/$res = Set::format($data, '{0}, {1}', array('{n}.Person.city', '{n}.Person.state'));
/*
Array
(
[0] => Boston, MA[1] => Boondock, TN[2] => Venice Beach, CA)*/$res = Set::format($data, '{{0}, {1}}', array('{n}.Person.city', '{n}.Person.state˓→'));/*
Array([0] => {Boston, MA}[1] => {Boondock, TN}[2] => {Venice Beach, CA})*/$res = Set::format($data, '{%2$d, %1$s}', array('{n}.Person.something', '{n}.Person.
˓→something'));/*Array(continues on next page)494

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)([0] => {42, 42}[1] => {0, {0}}[2] => {0, {1}})*/$res = Set::format($data, '%2$d, %1$s', array('{n}.Person.first_name', '{n}.Person.
˓→something'));
/*
Array
(
[0] => 42, Nate[1] => 0, Larry[2] => 0, Garrett)*/$res = Set::format($data, '%1$s, %2$d', array('{n}.Person.first_name', '{n}.Person.
˓→something'));
/*
Array
(
[0] => Nate, 42[1] => Larry, 0[2] => Garrett, 0)*/static Set::insert($list, $path, $data = null)
Return type
array
Inserts $data into an array as defined by $path.
$a = array('pages' => array('name' => 'page'));$result = Set::insert($a, 'files', array('name' => 'files'));/* $result now looks like:Array([pages] => Array([name] => page)
[files] => Array([name] => files))*/$a = array((continues on next page)

Utilities495

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'pages' => array('name' => 'page'));$result = Set::insert($a, 'pages.name', array());/* $result now looks like:Array([pages] => Array([name] => Array
(
)
)
)
*/
$a = array('pages' => array(0 => array('name' => 'main'),
1 => array('name' => 'about')));$result = Set::insert($a, 'pages.1.vars', array('title' => 'page title'));/* $result now looks like:Array([pages] => Array([0] => Array([name] => main)[1] => Array([name] => about[vars] => Array([title] => page title
)
)
)
)
*/
static Set::map($class = 'stdClass', $tmp = 'stdClass')
Return typeobjectThis method Maps the contents of the Set object to an object hierarchy while maintaining numeric keys as arraysof objects.
Basically, the map function turns array items into initialized class objects. By default it turns an array intoa stdClass Object, however you can map values into any type of class. Example: Set::map($array_of_values,‘nameOfYourClass’);:496

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x$data = array(
array("IndexedPage" => array("id" => 1,"url" => 'http://blah.com/','hash' => '68a9f053b19526d08e36c6a9ad150737933816a5','get_vars' => '','redirect' => '','created' => "1195055503",'updated' => "1195055503",)),
array("IndexedPage" => array("id" => 2,"url" => 'http://blah.com/','hash' => '68a9f053b19526d08e36c6a9ad150737933816a5','get_vars' => '','redirect' => '','created' => "1195055503",'updated' => "1195055503",),));$mapped = Set::map($data);/* $mapped now looks like:Array([0] => stdClass Object([_name_] => IndexedPage[id] => 1[url] => http://blah.com/[hash] => 68a9f053b19526d08e36c6a9ad150737933816a5[get_vars] =>[redirect] =>[created] => 1195055503[updated] => 1195055503)[1] => stdClass Object([_name_] => IndexedPage[id] => 2[url] => http://blah.com/[hash] => 68a9f053b19526d08e36c6a9ad150737933816a5[get_vars] =>[redirect] =>[created] => 1195055503[updated] => 1195055503)

Utilities(continues on next page)497

CakePHP Cookbook Documentation, Release 2.x(continued from previous page))*/
Using Set::map() with a custom class for second parameter:class MyClass {public function sayHi() {echo 'Hi!';}}$mapped = Set::map($data, 'MyClass');//Now you can access all the properties as in the example above,//but also you can call MyClass's methods$mapped->[0]->sayHi();
static Set::matches($conditions, $data = array(), $i = null, $length = null)
Return typeboolean
Set::matches can be used to see if a single item or a given xpath match certain conditions.
$a = array(
array('Article' => array('id' => 1, 'title' => 'Article 1')),array('Article' => array('id' => 2, 'title' => 'Article 2')),array('Article' => array('id' => 3, 'title' => 'Article 3')));$res = Set::matches(array('id>2'), $a[1]['Article']);// returns false$res = Set::matches(array('id>=2'), $a[1]['Article']);// returns true$res = Set::matches(array('id>=3'), $a[1]['Article']);// returns false$res = Set::matches(array('id<=2'), $a[1]['Article']);// returns true$res = Set::matches(array('id<2'), $a[1]['Article']);// returns false$res = Set::matches(array('id>1'), $a[1]['Article']);// returns true$res = Set::matches(array('id>1', 'id<3', 'id!=0'), $a[1]['Article']);// returns true$res = Set::matches(array('3'), null, 3);// returns true$res = Set::matches(array('5'), null, 5);// returns true$res = Set::matches(array('id'), $a[1]['Article']);// returns true$res = Set::matches(array('id', 'title'), $a[1]['Article']);// returns true$res = Set::matches(array('non-existent'), $a[1]['Article']);(continues on next page)

498

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// returns false$res = Set::matches('/Article[id=2]', $a);// returns true$res = Set::matches('/Article[id=4]', $a);// returns false$res = Set::matches(array(), $a);// returns true
static Set::merge($arr1, $arr2 = null)
Return typearray
This function can be thought of as a hybrid between PHP’s array_merge and array_merge_recursive. The difference to the two is that if an array key contains another array then the function behaves recursive (unlike
array_merge) but does not do if for keys containing strings (unlike array_merge_recursive). See the unit test for
more information.
Note: This function will work with an unlimited amount of arguments and typecasts non-array parameters into
arrays.
$arry1 = array(
array('id' => '48c2570e-dfa8-4c32-a35e-0d71cbdd56cb','name' => 'mysql raleigh-workshop-08 < 2008-09-05.sql ','description' => 'Importing an sql dump'),
array('id' => '48c257a8-cf7c-4af2-ac2f-114ecbdd56cb','name' => 'pbpaste | grep -i Unpaid | pbcopy','description' => 'Remove all lines that say "Unpaid".',));$arry2 = 4;
$arry3 = array(0 => 'test array', 'cats' => 'dogs', 'people' => 1267);$arry4 = array('cats' => 'felines', 'dog' => 'angry');$res = Set::merge($arry1, $arry2, $arry3, $arry4);/* $res now looks like:Array([0] => Array([id] => 48c2570e-dfa8-4c32-a35e-0d71cbdd56cb[name] => mysql raleigh-workshop-08 < 2008-09-05.sql[description] => Importing an sql dump)
[1] => Array([id] => 48c257a8-cf7c-4af2-ac2f-114ecbdd56cb(continues on next page)

Utilities499

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[name] => pbpaste | grep -i Unpaid | pbcopy[description] => Remove all lines that say "Unpaid".
)[2] => 4[3] => test array[cats] => felines[people] => 1267
[dog] => angry)*/
static Set::nest($data, $options = array())
Return typearray
Takes in a flat array and returns a nested array:$data = array(
array('ModelName' => array('id' => 1, 'parent_id' => null)),array('ModelName' => array('id' => 2, 'parent_id' => 1)),array('ModelName' => array('id' => 3, 'parent_id' => 1)),array('ModelName' => array('id' => 4, 'parent_id' => 1)),array('ModelName' => array('id' => 5, 'parent_id' => 1)),array('ModelName' => array('id' => 6, 'parent_id' => null)),array('ModelName' => array('id' => 7, 'parent_id' => 6)),array('ModelName' => array('id' => 8, 'parent_id' => 6)),array('ModelName' => array('id' => 9, 'parent_id' => 6)),array('ModelName' => array('id' => 10, 'parent_id' => 6)));$result = Set::nest($data, array('root' => 6));/* $result now looks like:array((int) 0 => array('ModelName' => array('id' => (int) 6,'parent_id' => null),'children' => array((int) 0 => array('ModelName' => array('id' => (int) 7,'parent_id' => (int) 6),'children' => array()),(int) 1 => array('ModelName' => array('id' => (int) 8,'parent_id' => (int) 6(continues on next page)

500

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)),'children' => array()),(int) 2 => array('ModelName' => array('id' => (int) 9,'parent_id' => (int) 6),'children' => array()),(int) 3 => array('ModelName' => array('id' => (int) 10,'parent_id' => (int) 6),'children' => array()
)
)
)
) */
static Set::normalize($list, $assoc = true, $sep = ', ', $trim = true)
Return typearray
Normalizes a string or array list.
$a = array('Tree','CounterCache','Upload' => array('folder' => 'products','fields' => array('image_1_id', 'image_2_id', 'image_3_id', 'image_4_id',
˓→'image_5_id')));$b = array('Cacheable' => array('enabled' => false),'Limit','Bindable','Validator','Transactional');$result = Set::normalize($a);/* $result now looks like:
Array([Tree] =>[CounterCache] =>[Upload] => Array([folder] => products(continues on next page)

Utilities

501

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[fields] => Array([0] => image_1_id[1] => image_2_id[2] => image_3_id[3] => image_4_id[4] => image_5_id)))*/$result = Set::normalize($b);/* $result now looks like:
Array([Cacheable] => Array([enabled] =>)[Limit] =>[Bindable] =>[Validator] =>[Transactional] =>)*/$result = Set::merge($a, $b);/* $result now looks like:Array([0] => Tree[1] => CounterCache[Upload] => Array([folder] => products[fields] => Array([0] => image_1_id[1] => image_2_id[2] => image_3_id[3] => image_4_id[4] => image_5_id))[Cacheable] => Array([enabled] =>)[2] => Limit[3] => Bindable[4] => Validator(continues on next page)502

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[5] => Transactional)*/$result = Set::normalize(Set::merge($a, $b)); // Now merge the two and normalize/* $result now looks like:
Array([Tree] =>[CounterCache] =>[Upload] => Array([folder] => products[fields] => Array([0] => image_1_id[1] => image_2_id[2] => image_3_id[3] => image_4_id[4] => image_5_id))[Cacheable] => Array([enabled] =>)[Limit] =>[Bindable] =>[Validator] =>[Transactional] =>)*/static Set::numeric($array=null)
Return typebooleanChecks to see if all the values in the array are numeric:$data = array('one');$res = Set::numeric(array_keys($data));// $res is true
$data = array(1 => 'one');$res = Set::numeric($data);// $res is false$data = array('one');$res = Set::numeric($data);(continues on next page)

Utilities503

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// $res is false$data = array('one' => 'two');$res = Set::numeric($data);// $res is false$data = array('one' => 1);$res = Set::numeric($data);// $res is true
$data = array(0);$res = Set::numeric($data);// $res is true
$data = array('one', 'two', 'three', 'four', 'five');$res = Set::numeric(array_keys($data));// $res is true
$data = array(1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five');$res = Set::numeric(array_keys($data));// $res is true
$data = array('1' => 'one', 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five');$res = Set::numeric(array_keys($data));// $res is true
$data = array('one', 2 => 'two', 3 => 'three', 4 => 'four', 'a' => 'five');$res = Set::numeric(array_keys($data));// $res is false
static Set::pushDiff($array1, $array2)
Return typearray
This function merges two arrays and pushes the differences in array2 to the bottom of the resultant array.
Example 1$array1 = array('ModelOne' => array('id' => 1001, 'field_one' => 'a1.m1.f1', 'field_
˓→two' => 'a1.m1.f2'));$array2 = array('ModelOne' => array('id' => 1003, 'field_one' => 'a3.m1.f1', 'field_
˓→two' => 'a3.m1.f2', 'field_three' => 'a3.m1.f3'));$res = Set::pushDiff($array1, $array2);/* $res now looks like:(continues on next page)504

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

Array([ModelOne] => Array([id] => 1001
[field_one] => a1.m1.f1[field_two] => a1.m1.f2[field_three] => a3.m1.f3))*/
Example 2$array1 = array("a" => "b", 1 => 20938, "c" => "string");$array2 = array("b" => "b", 3 => 238, "c" => "string", array("extra_field"));$res = Set::pushDiff($array1, $array2);/* $res now looks like:Array([a] => b[1] => 20938[c] => string[b] => b[3] => 238[4] => Array([0] => extra_field))*/static Set::remove($list, $path = null)
Return typearray
Removes an element from a Set or array as defined by $path:$a = array('pages' => array('name' => 'page'),'files' => array('name' => 'files'));$result = Set::remove($a, 'files');/* $result now looks like:Array([pages] => Array([name] => page)(continues on next page)

Utilities505

CakePHP Cookbook Documentation, Release 2.x(continued from previous page))*/
static Set::reverse($object)
Return typearray
Set::reverse is basically the opposite of Set::map. It converts an object into an array. If $object is not an object,
reverse will simply return $object.
$result = Set::reverse(null);// Null$result = Set::reverse(false);// false$a = array('Post' => array('id' => 1, 'title' => 'First Post'),'Comment' => array(
array('id' => 1, 'title' => 'First Comment'),array('id' => 2, 'title' => 'Second Comment')),'Tag' => array(
array('id' => 1, 'title' => 'First Tag'),array('id' => 2, 'title' => 'Second Tag')),);$map = Set::map($a); // Turn $a into a class object/* $map now looks like:
stdClass Object([_name_] => Post[id] => 1[title] => First Post
[Comment] => Array([0] => stdClass Object([id] => 1[title] => First Comment)[1] => stdClass Object([id] => 2[title] => Second Comment))[Tag] => Array([0] => stdClass Object([id] => 1[title] => First Tag)(continues on next page)

506

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[1] => stdClass Object([id] => 2[title] => Second Tag)))*/$result = Set::reverse($map);/* $result now looks like:
Array([Post] => Array([id] => 1[title] => First Post
[Comment] => Array([0] => Array([id] => 1[title] => First Comment)[1] => Array([id] => 2[title] => Second Comment))[Tag] => Array([0] => Array([id] => 1[title] => First Tag)[1] => Array([id] => 2[title] => Second Tag
)
)
)
)
*/
$result = Set::reverse($a['Post']); // Just return the array/* $result now looks like:Array([id] => 1(continues on next page)

Utilities507

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)[title] => First Post)*/
static Set::sort($data, $path, $dir)
Return typearray
Sorts an array by any value, determined by a Set-compatible path:$a = array(0 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name'␣
˓→=> 'Nate'))),
1 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name'␣
˓→=> 'Lindsay'))),
2 => array('Person' => array('name' => 'Adam'),'Friend' => array(array('name' =>˓→ 'Bob'))));$result = Set::sort($a, '{n}.Person.name', 'asc');/* $result now looks like:array(
0 => array('Person' => array('name' => 'Adam'),'Friend' => array(array('name' => 'Bob˓→'))),
1 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate˓→'))),
2 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' =>˓→'Lindsay'))));*/$result = Set::sort($a, '{n}.Person.name', 'desc');/* $result now looks like:array(
2 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' =>˓→'Lindsay')))1 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate˓→'))),
0 => array('Person' => array('name' => 'Adam'),'Friend' => array(array('name' => 'Bob˓→'))),);*/

508

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

CakeText
class CakeText
The CakeText class includes convenience methods for creating and manipulating strings and is normally accessed
statically. Example: CakeText::uuid().
Deprecated since version 2.7: The String class was deprecated in 2.7 in favour of the CakeText class. While theString class is still available for backwards compatibility, using CakeText is recommended as it offers compatibility
with PHP7 and HHVM.
If you need TextHelper functionalities outside of a View, use the CakeText class:class UsersController extends AppController {public $components = array('Auth');public function afterLogin() {App::uses('CakeText', 'Utility');$message = $this->User->find('new_message');if (!empty($message)) {// notify user of new message$this->Session->setFlash(__('You have a new message: %s', CakeText::truncate(
˓→$message['Message']['body'], 255, array('html' => true))));}}}
Changed in version 2.1: Several methods from TextHelper have been moved to CakeText class.
static CakeText::uuid
The UUID method is used to generate unique identifiers as per RFC 412295 . The UUID is a 128bit string in theformat of 485fc381-e790-47a3-9794-1337c0a8fe68.
CakeText::uuid(); // 485fc381-e790-47a3-9794-1337c0a8fe68
static CakeText::tokenize($data, $separator = ', ', $leftBound = '(', $rightBound = ')')
Tokenizes a string using $separator, ignoring any instance of $separator that appears between $leftBoundand $rightBound.
This method can be useful when splitting up data in that has regular formatting such as tag lists:$data = "cakephp 'great framework' php";$result = CakeText::tokenize($data, ' ', "'", "'");// result contains
array('cakephp', "'great framework'", 'php');
static CakeText::insert($string, $data, $options = array())
The insert method is used to create string templates and to allow for key/value replacements:
CakeText::insert('My name is :name and I am :age years old.', array('name' => 'Bob',
˓→ 'age' => '65'));// generates: "My name is Bob and I am 65 years old."
95 https://datatracker.ietf.org/doc/html/rfc4122.html

Utilities509

CakePHP Cookbook Documentation, Release 2.x

static CakeText::cleanInsert($string, $options = array())
Cleans up a CakeText::insert formatted string with given $options depending on the ‘clean’ key in $options.
The default method used is text but html is also available. The goal of this function is to replace all whitespace
and unneeded markup around placeholders that did not get replaced by Set::insert.
You can use the following options in the options array:$options = array('clean' => array('method' => 'text', // or html),
'before' => '','after' => '');static CakeText::wrap($text, $options = array())
Wraps a block of text to a set width, and indent blocks as well. Can intelligently wrap text so words are not sliced
across lines:$text = 'This is the song that never ends.';$result = CakeText::wrap($text, 22);// returns
This is the songthat never ends.
You can provide an array of options that control how wrapping is done. The supported options are:• width The width to wrap to. Defaults to 72.
• wordWrap Whether or not to wrap whole words. Defaults to true.
• indent The character to indent lines with. Defaults to ‘’.
• indentAt The line number to start indenting text. Defaults to 0.
CakeText::highlight(string $haystack, string $needle, array $options = array())
Parameters• $haystack (string) – The string to search.
• $needle (string) – The string to find.
• $options (array) – An array of options, see below.
Highlights $needle in $haystack using the $options['format'] string specified or a default string.
Options:• ‘format’ - string The piece of HTML with that the phrase will be highlighted• ‘html’ - bool If true, will ignore any HTML tags, ensuring that only the correct text is highlighted
Example:// called as TextHelper
echo $this->Text->highlight($lastSentence,'using',array('format' => '<span class="highlight">\1</span>')(continues on next page)

510

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page));// called as CakeText
App::uses('CakeText', 'Utility');echo CakeText::highlight($lastSentence,'using',array('format' => '<span class="highlight">\1</span>'));Output:Highlights $needle in $haystack <span class="highlight">using</span>
the $options['format'] string specified or a default string.
CakeText::stripLinks($text)
Strips the supplied $text of any HTML links.
CakeText::truncate(string $text, int $length=100, array $options)Parameters• $text (string) – The text to truncate.
• $length (int) – The length, in characters, beyond which the text should be truncated.
• $options (array) – An array of options to use.
If $text is longer than $length characters, this method truncates it at $length and adds a suffix consisting of'ellipsis', if defined. If 'exact' is passed as false, the truncation will occur at the first whitespace after
the point at which $length is exceeded. If 'html' is passed as true, HTML tags will be respected and will
not be cut off.
$options is used to pass all extra parameters, and has the following possible keys by default, all of which are
optional:
array('ellipsis' => '...','exact' => true,'html' => false)
Example:// called as TextHelper
echo $this->Text->truncate('The killer crept forward and tripped on the rug.',22,
array('ellipsis' => '...','exact' => false));// called as CakeText
App::uses('CakeText', 'Utility');(continues on next page)

Utilities

511

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)echo CakeText::truncate('The killer crept forward and tripped on the rug.',22,
array('ellipsis' => '...','exact' => false));
Output:The killer crept...
Changed in version 2.3: ending has been replaced by ellipsis. ending is still used in 2.2.1
CakeText::tail(string $text, int $length=100, array $options)Parameters• $text (string) – The text to truncate.
• $length (int) – The length, in characters, beyond which the text should be truncated.
• $options (array) – An array of options to use.
If $text is longer than $length characters, this method removes an initial substring with length consisting of
the difference and prepends a prefix consisting of 'ellipsis', if defined. If 'exact' is passed as false, thetruncation will occur at the first whitespace prior to the point at which truncation would otherwise take place.
$options is used to pass all extra parameters, and has the following possible keys by default, all of which are
optional:
array('ellipsis' => '...','exact' => true)
New in version 2.3.
Example:$sampleText = 'I packed my bag and in it I put a PSP, a PS3, a TV, ' .
'a C# program that can divide by zero, death metal t-shirts'// called as TextHelper
echo $this->Text->tail($sampleText,70,
array('ellipsis' => '...','exact' => false));// called as CakeText
App::uses('CakeText', 'Utility');(continues on next page)

512

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)echo CakeText::tail($sampleText,70,
array('ellipsis' => '...','exact' => false));
Output:...a TV, a C# program that can divide by zero, death metal t-shirts
CakeText::excerpt(string $haystack, string $needle, integer $radius=100, string $ellipsis="...")
Parameters• $haystack (string) – The string to search.
• $needle (string) – The string to excerpt around.
• $radius (int) – The number of characters on either side of $needle you want to include.
• $ellipsis (string) – Text to append/prepend to the beginning or end of the result.
Extracts an excerpt from $haystack surrounding the $needle with a number of characters on each side determined by $radius, and prefix/suffix with $ellipsis. This method is especially handy for search results. The
query string or keywords can be shown within the resulting document.
// called as TextHelper
echo $this->Text->excerpt($lastParagraph, 'method', 50, '...');// called as CakeText
App::uses('CakeText', 'Utility');echo CakeText::excerpt($lastParagraph, 'method', 50, '...');
Output:... by $radius, and prefix/suffix with $ellipsis. This method is
especially handy for search results. The query...
CakeText::toList(array $list, $and='and')Parameters• $list (array) – Array of elements to combine into a list sentence.
• $and (string) – The word used for the last join.
Creates a comma-separated list where the last two items are joined with ‘and’.
// called as TextHelper
echo $this->Text->toList($colors);// called as CakeText
App::uses('CakeText', 'Utility');echo CakeText::toList($colors);

Utilities

513

CakePHP Cookbook Documentation, Release 2.x

Output:red, orange, yellow, green, blue, indigo and violet

CakeTimeclass CakeTime
If you need TimeHelper functionalities outside of a View, use the CakeTime class:class UsersController extends AppController {public $components = array('Auth');public function afterLogin() {App::uses('CakeTime', 'Utility');if (CakeTime::isToday($this->Auth->user('date_of_birth']))) {// greet user with a happy birthday message$this->Session->setFlash(__('Happy birthday you...'));}}}New in version 2.1: CakeTime has been factored out from TimeHelper.
Formatting
CakeTime::convert($serverTime, $timezone = NULL)
Return type
integerConverts given time (in server’s time zone) to user’s local time, given his/her timezone.
// called via TimeHelper
echo $this->Time->convert(time(), 'Asia/Jakarta');// 1321038036// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::convert(time(), new DateTimeZone('Asia/Jakarta'));Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
CakeTime::convertSpecifiers($format, $time = NULL)
Return typestringConverts a string representing the format for the function strftime and returns a Windows safe and i18n aware
format.
CakeTime::dayAsSql($dateString, $field_name, $timezone = NULL)
Return typestring514

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Creates a string in the same format as daysAsSql but only needs a single date object:// called via TimeHelper
echo $this->Time->dayAsSql('Aug 22, 2011', 'modified');// (modified >= '2011-08-22 00:00:00') AND// (modified <= '2011-08-22 23:59:59')// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::dayAsSql('Aug 22, 2011', 'modified');Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
CakeTime::daysAsSql($begin, $end, $fieldName, $timezone = NULL)
Return typestringReturns a string in the format “($field_name >= ‘2008-01-21 00:00:00’) AND ($field_name <= ‘2008-01-25
23:59:59’)”. This is handy if you need to search for records between two dates inclusively:// called via TimeHelper
echo $this->Time->daysAsSql('Aug 22, 2011', 'Aug 25, 2011', 'created');// (created >= '2011-08-22 00:00:00') AND// (created <= '2011-08-25 23:59:59')// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::daysAsSql('Aug 22, 2011', 'Aug 25, 2011', 'created');Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
CakeTime::format($date, $format = NULL, $default = false, $timezone = NULL)
Return typestringWill return a string formatted to the given format using the PHP strftime() formatting options96 :// called via TimeHelper
echo $this->Time->format('2011-08-22 11:53:00', '%B %e, %Y %H:%M %p');// August 22, 2011 11:53 AM
echo $this->Time->format('+2 days', '%c');// 2 days from now formatted as Sun, 13 Nov 2011 03:36:10 AM EET// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::format('2011-08-22 11:53:00', '%B %e, %Y %H:%M %p');
echo CakeTime::format('+2 days', '%c');
You can also provide the date/time as the first argument. When doing this you should use strftime compatible
formatting. This call signature allows you to leverage locale aware date formatting which is not possible using
date() compatible formatting:Utilities

515

CakePHP Cookbook Documentation, Release 2.x// called via TimeHelper
echo $this->Time->format('2012-01-13', '%d-%m-%Y', 'invalid');// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::format('2011-08-22', '%d-%m-%Y');
Changed in version 2.2: $format and $date parameters are in opposite order as used in 2.1 and below.
$timezone parameter replaces $userOffset parameter used in 2.1 and below. $default parameter replaces$invalid parameter used in 2.1 and below.
New in version 2.2: $date parameter now also accepts a DateTime object.
CakeTime::fromString($dateString, $timezone = NULL)
Return typestringTakes a string and uses strtotime97 to convert it into a date integer:// called via TimeHelper
echo $this->Time->fromString('Aug 22, 2011');// 1313971200
echo $this->Time->fromString('+1 days');// 1321074066 (+1 day from current date)// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::fromString('Aug 22, 2011');echo CakeTime::fromString('+1 days');
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
CakeTime::gmt($dateString = NULL)
Return type
integerWill return the date as an integer set to Greenwich Mean Time (GMT).
// called via TimeHelper
echo $this->Time->gmt('Aug 22, 2011');// 1313971200// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::gmt('Aug 22, 2011');
CakeTime::i18nFormat($date, $format = NULL, $invalid = false, $timezone = NULL)
Return typestring96 https://www.php.net/manual/en/function.strftime.php
97 https://us.php.net/manual/en/function.date.php

516

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Returns a formatted date string, given either a UNIX timestamp or a valid strtotime() date string. It take in accountthe default date format for the current language if a LC_TIME file is used. For more info about LC_TIME file
check here.
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
CakeTime::nice($dateString = NULL, $timezone = NULL, $format = null)
Return typestringTakes a date string and outputs it in the format “Tue, Jan 1st 2008, 19:25” or as per optional $format parampassed:// called via TimeHelper
echo $this->Time->nice('2011-08-22 11:53:00');// Mon, Aug 22nd 2011, 11:53// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::nice('2011-08-22 11:53:00');
CakeTime::niceShort($dateString = NULL, $timezone = NULL)
Return typestringTakes a date string and outputs it in the format “Jan 1st 2008, 19:25”. If the date object is today, the format willbe “Today, 19:25”. If the date object is yesterday, the format will be “Yesterday, 19:25”:// called via TimeHelper
echo $this->Time->niceShort('2011-08-22 11:53:00');// Aug 22nd, 11:53// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::niceShort('2011-08-22 11:53:00');
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
CakeTime::serverOffset()
Return type
integer
Returns server’s offset from GMT in seconds.
CakeTime::timeAgoInWords($dateString, $options = array())
Return typestringWill take a datetime string (anything that is parsable by PHP’s strtotime() function or MySQL’s datetime format)
and convert it into a friendly word format like, “3 weeks, 3 days ago”:// called via TimeHelper
echo $this->Time->timeAgoInWords('Aug 22, 2011');(continues on next page)

Utilities

517

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// on 22/8/11// on August 22nd, 2011
echo $this->Time->timeAgoInWords('Aug 22, 2011',
array('format' => 'F jS, Y'));// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::timeAgoInWords('Aug 22, 2011');echo CakeTime::timeAgoInWords('Aug 22, 2011',
array('format' => 'F jS, Y'));
Use the ‘end’ option to determine the cutoff point to no longer will use words; default ‘+1 month’:// called via TimeHelper
echo $this->Time->timeAgoInWords('Aug 22, 2011',
array('format' => 'F jS, Y', 'end' => '+1 year'));// On Nov 10th, 2011 it would display: 2 months, 2 weeks, 6 days ago// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::timeAgoInWords('Aug 22, 2011',
array('format' => 'F jS, Y', 'end' => '+1 year'));
Use the ‘accuracy’ option to determine how precise the output should be. You can use this to limit the output:// If $timestamp is 1 month, 1 week, 5 days and 6 hours ago
echo CakeTime::timeAgoInWords($timestamp, array('accuracy' => array('month' => 'month'),'end' => '1 year'));// Outputs '1 month ago'
Changed in version 2.2: The accuracy option was added.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
CakeTime::toAtom($dateString, $timezone = NULL)
Return typestringWill return a date string in the Atom format “2008-01-12T00:00:00Z”
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.

518

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

CakeTime::toQuarter($dateString, $range = false)
Return type
mixed
Will return 1, 2, 3 or 4 depending on what quarter of the year the date falls in. If range is set to true, a two element
array will be returned with start and end dates in the format “2008-03-31”:// called via TimeHelper
echo $this->Time->toQuarter('Aug 22, 2011');// Would print 3$arr = $this->Time->toQuarter('Aug 22, 2011', true);
/*
Array
(
[0] => 2011-07-01[1] => 2011-09-30)*/// called as CakeTime
App::uses('CakeTime', 'Utility');echo CakeTime::toQuarter('Aug 22, 2011');$arr = CakeTime::toQuarter('Aug 22, 2011', true);
New in version 2.2: $dateString parameter now also accepts a DateTime object.
New in version 2.4: The new option parameters relativeString (defaults to %s ago) and absoluteString(defaults to on %s) to allow customization of the resulting output string are now available.
CakeTime::toRSS($dateString, $timezone = NULL)
Return typestringWill return a date string in the RSS format “Sat, 12 Jan 2008 00:00:00 -0500”
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
CakeTime::toUnix($dateString, $timezone = NULL)
Return type
integer
A wrapper for fromString.
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
CakeTime::toServer($dateString, $timezone = NULL, $format = 'Y-m-d H:i:s')
Return typemixed
New in version 2.2: Returns a formatted date in server’s timezone.

Utilities

519

CakePHP Cookbook Documentation, Release 2.x

CakeTime::timezone($timezone = NULL)
Return type
DateTimeZone
New in version 2.2: Returns a timezone object from a string or the user’s timezone object. If the function iscalled without a parameter it tries to get timezone from ‘Config.timezone’ configuration variable.
CakeTime::listTimezones($filter = null, $country = null, $options = array())
Return typearray
New in version 2.2: Returns a list of timezone identifiers.
Changed in version 2.8: $options now accepts array with group, abbr, before, and after keys. Specify
abbr => true will append the timezone abbreviation in the <option> text.
Testing Time
CakeTime::isToday($dateString, $timezone = NULL)
CakeTime::isThisWeek($dateString, $timezone = NULL)
CakeTime::isThisMonth($dateString, $timezone = NULL)
CakeTime::isThisYear($dateString, $timezone = NULL)
CakeTime::wasYesterday($dateString, $timezone = NULL)
CakeTime::isTomorrow($dateString, $timezone = NULL)
CakeTime::isFuture($dateString, $timezone = NULL)
New in version 2.4.
CakeTime::isPast($dateString, $timezone = NULL)
New in version 2.4.
CakeTime::wasWithinLast($timeInterval, $dateString, $timezone = NULL)
Changed in version 2.2: $timezone parameter replaces $userOffset parameter used in 2.1 and below.
New in version 2.2: $dateString parameter now also accepts a DateTime object.
All of the above functions return true or false when passed a date string. wasWithinLast takes an additional$timeInterval option:// called via TimeHelper$this->Time->wasWithinLast($timeInterval, $dateString);// called as CakeTime
App::uses('CakeTime', 'Utility');
CakeTime::wasWithinLast($timeInterval, $dateString);
wasWithinLast takes a time interval which is a string in the format “3 months” and accepts a time interval of
seconds, minutes, hours, days, weeks, months and years (plural and not). If a time interval is not recognized (forexample, if it is mistyped) then it will default to days.

520

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Xml
class Xml
The Xml class was all refactored. PHP 5 implements a SimpleXML98 and DOMDocument99 , so that CakePHP doesn’t
need to re-implement an XML parser. The new XML class will transform an array into a SimpleXMLElement or
DOMDocument objects, and vice versa.
Importing data to Xml class
In CakePHP 1.3 you can pass an array, XML as string, URL or file path to the constructor of the Xml class in order toimport data. In CakePHP 2.0 you can do it by using Xml::build(). Unless the return value is a Xml object, it will
return a SimpleXMLElement or DOMDocument object (depending of your options parameter - default is SimpleXMLElement). Below you get some samples on how to import data from URL:// First Load the Utility Class
App::uses('Xml', 'Utility');// Old method:$xml = new Xml('https://bakery.cakephp.org/articles.rss');// New method using SimpleXML$xml = Xml::build('https://bakery.cakephp.org/articles.rss');// $xml now is a instance of SimpleXMLElement// or$xml = Xml::build('https://bakery.cakephp.org/articles.rss', array('return' => 'simplexml˓→'));// $xml now is a instance of SimpleXMLElement// New method using DOMDocument$xml = Xml::build('https://bakery.cakephp.org/articles.rss', array('return' =>˓→'domdocument'));// $xml now is a instance of DOMDocument
You can use Xml::build() to build XML objects from a variety of sources. You can use XML to build objects fromstring data:$text = '<?xml version="1.0" encoding="utf-8"?><post><id>1</id><title>Best post</title><body> ... </body></post>';$xml = Xml::build($text);
You can also build Xml objects from either local files, or remote files. Remote files will be fetched with HttpSocket:// local file
$xml = Xml::build('/home/awesome/unicorns.xml');(continues on next page)
98 https://www.php.net/simplexml

99 https://www.php.net/domdocument

Utilities

521

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// remote file
$xml = Xml::build('https://bakery.cakephp.org/articles.rss');
You can also build Xml objects using an array:$data = array('post' => array('id' => 1,'title' => 'Best post','body' => ' ... '));$xml = Xml::build($data);
If your input is invalid the Xml class will throw a Exception:$xmlString = 'What is XML?'try {$xmlObject = Xml::build($xmlString); // Here will throw a Exception} catch (XmlException $e) {throw new InternalErrorException();}Note: DOMDocument100 and SimpleXML101 implement different API’s. Be sure to use the correct methods on theobject you request from Xml.

Transforming a XML string in array
Converting XML strings into arrays is simple with the Xml class as well. By default you’ll get a SimpleXml object
back://Old method:$xmlString = '<?xml version="1.0"?><root><child>value</child></root>';$xmlObject = new Xml($xmlString);$xmlArray = $xmlObject->toArray();// New method:$xmlString = '<?xml version="1.0"?><root><child>value</child></root>';$xmlArray = Xml::toArray(Xml::build($xmlString));
If your XML is invalid it will throw an Exception.
100 https://www.php.net/domdocument
101 https://www.php.net/simplexml

522

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Transforming an array into a string of XML// Old method:$xmlArray = array('root' => array('child' => 'value'));$xmlObject = new Xml($xmlArray, array('format' => 'tags'));$xmlString = $xmlObject->toString();// New method:$xmlArray = array('root' => array('child' => 'value'));$xmlObject = Xml::fromArray($xmlArray, array('format' => 'tags')); // You can use␣˓→Xml::build() too$xmlString = $xmlObject->asXML();Your array must have only one element in the “top level” and it can not be numeric. If the array is not in this format,
Xml will throw an Exception. Examples of invalid arrays:// Top level with numeric key
array(
array('key' => 'value'));// Multiple keys in top level
array('key1' => 'first value','key2' => 'other value');

Warning: The default format option was changed from attributes to tags. This was done to make the Xml, that the
Xml class generates, more compatible with XML in the wild. Be careful if you depend of this. In the new version
you can create a mixed array with tags, attributes and value, just use format as tags (or do not say anything, because
it is the default value) and prefix keys that are supposed to be attributes with @. For value text, use @ as the key.
$xmlArray = array('project' => array('@id' => 1,'name' => 'Name of project, as tag','@' => 'Value of project'));$xmlObject = Xml::fromArray($xmlArray);$xmlString = $xmlObject->asXML();The content of $xmlString will be:<?xml version="1.0"?><project id="1">Value of project<name>Name of project, as tag</name></project>

Note: The structure of array was changed. Now the child must be in a sub-tree and not in the same tree. Moreover,the strings will not be changed by Inflector. See the sample below:

Utilities523

CakePHP Cookbook Documentation, Release 2.x$oldArray = array('Projects' => array(
array('Project' => array('id' => 1, 'title' => 'Project 1'),
'Industry' => array('id' => 1, 'name' => 'Industry 1')),
array('Project' => array('id' => 2, 'title' => 'Project 2'),
'Industry' => array('id' => 2, 'name' => 'Industry 2'))));$newArray = array('projects' => array('project' => array(
array('id' => 1, 'title' => 'Project 1','industry' => array('id' => 1, 'name' => 'Industry 1')),
array('id' => 2, 'title' => 'Project 2','industry' => array('id' => 2, 'name' => 'Industry 2')))));
Both will result the XML below:<?xml version="1.0"?><projects><project><id>1</id><title>Project 1</title><industry><id>1</id><name>Industry 1</name></industry></project><project><id>2</id><title>Project 2</title><industry><id>2</id><name>Industry 2</name></industry></project></projects>

524

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

Using Namespaces
To use XML Namespaces in your array, you must create a key with name xmlns: to generic namespace or input theprefix xmlns: in a custom namespace. See the samples:$xmlArray = array('root' => array('xmlns:' => 'https://cakephp.org','child' => 'value'));$xml1 = Xml::fromArray($xmlArray);$xmlArray('root' => array('tag' => array('xmlns:pref' => 'https://cakephp.org','pref:item' => array('item 1','item 2'))));
$xml2 = Xml::fromArray($xmlArray);
The value of $xml1 and $xml2 will be, respectively:<?xml version="1.0"?><root xmlns="https://cakephp.org"><child>value</child><?xml version="1.0"?><root><tag xmlns:pref="https://cakephp.org"><pref:item>item 1</pref:item><pref:item>item␣
˓→2</pref:item></tag></root>

Creating a child
The Xml class of CakePHP 2.0 doesn’t provide the manipulation of content, this must be done using SimpleXMLElement or DOMDocument. But CakePHP has you covered. Below you see the steps for creating a child node with
CakePHP:// CakePHP 1.3$myXmlOriginal = '<?xml version="1.0"?><root><child>value</child></root>';$xml = new Xml($myXmlOriginal, array('format' => 'tags'));$xml->children[0]->createNode('young', 'new value');// CakePHP 2.0 - Using SimpleXML$myXmlOriginal = '<?xml version="1.0"?><root><child>value</child></root>';$xml = Xml::build($myXmlOriginal);$xml->root->addChild('young', 'new value');(continues on next page)

Utilities525

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// CakePHP 2.0 - Using DOMDocument$myXmlOriginal = '<?xml version="1.0"?><root><child>value</child></root>';$xml = Xml::build($myXmlOriginal, array('return' => 'domdocument'));$child = $xml->createElement('young', 'new value');$xml->firstChild->appendChild($child);

Tip: After manipulating your XML using SimpleXMLElement or DomDocument you can use Xml::toArray()without problem.

Xml API
A factory and conversion class for creating SimpleXml or DOMDocument objects from a number of sources including
strings, arrays and remote URLs.
static Xml::build($input, $options = array())
Initialize SimpleXMLElement or DOMDocument from a given XML string, file path, URL or array
Building XML from a string:$xml = Xml::build('<example>text</example>');Building XML from string (output DOMDocument):$xml = Xml::build('<example>text</example>', array('return' => 'domdocument'));Building XML from a file path:$xml = Xml::build('/path/to/an/xml/file.xml');
Building from a remote URL:
$xml = Xml::build('http://example.com/example.xml');
Building from an array:$value = array('tags' => array('tag' => array(
array('id' => '1',
'name' => 'defect'),
array('id' => '2',
'name' => 'enhancement'))));$xml = Xml::build($value);
When building XML from an array ensure that there is only one top level element.
526

Chapter 7. Core Libraries

CakePHP Cookbook Documentation, Release 2.x

static Xml::toArray($obj)
Convert either a SimpleXml or DOMDocument object into an array.

Utilities

527

CakePHP Cookbook Documentation, Release 2.x

528

Chapter 7. Core Libraries

CHAPTER 8

Plugins

CakePHP allows you to set up a combination of controllers, models, and views and release them as a packaged application plugin that others can use in their CakePHP applications. Have a sweet user management module, simple
blog, or web services module in one of your applications? Package it as a CakePHP plugin so you can pop it into otherapplications.
The main tie between a plugin and the application it has been installed into, is the application’s configuration (databaseconnection, etc.). Otherwise, it operates in its own little space, behaving much like it would if it were an application
on its own.

How To Install Plugins
There are four ways to install a CakePHP plugin:• Through Composer• Manually• As Git Submodule• By Git Cloning
But don’t forget to Enable the Plugin afterwards.

529

CakePHP Cookbook Documentation, Release 2.x

Manually
To install a plugin manually, you just have to drop the plugin folder into your app/Plugin/ folder. If you’re installing aplugin named ‘ContactManager’ then you should have a folder in app/Plugin/ named ‘ContactManager’ under which
are the plugin’s View, Model, Controller, webroot and any other directories.

ComposerIf you aren’t familiar with the dependency management tool named Composer, take the time to read the Composer
documentation102 .
To install the imaginary plugin ‘ContactManager’ through Composer, add it as dependency to your project’s composer.
json:{"require": {"cakephp/contact-manager": "1.2.*"}}
If a CakePHP plugin is of the type cakephp-plugin, as it should, Composer will install it inside your /Plugin directory,
rather than in the usual vendors folder.
Note: Consider using “require-dev” if you only want to include the plugin for your development environment.
Alternatively, you can use the Composer CLI tool to require103 (and install) plugins:php composer.phar require cakephp/contact-manager:1.2.*

Git Clone
If the plugin you want to install is hosted as a Git repo, you can also clone it. Let’s assume the imaginary plugin‘ContactManager’ is hosted on GitHub. You could clone it by executing the following command in your app/Plugin/folder:git clone git://github.com/cakephp/contact-manager.git ContactManager

Git Submodule
If the plugin you want to install is hosted as a Git repo, but you prefer not to clone it, you can also integrate it as a Git
submodule. Execute the following commands in your app folder:
git submodule add git://github.com/cakephp/contact-manager.git Plugin/ContactManager
git submodule init
git submodule update
102 https://getcomposer.org/doc/00-intro.md

103 https://getcomposer.org/doc/03-cli.md#require

530

Chapter 8. Plugins

CakePHP Cookbook Documentation, Release 2.x

Enable the Plugin
Plugins need to be loaded manually in app/Config/bootstrap.php.
You can either load one by one or all of them in a single call:
CakePlugin::loadAll(); // Loads all plugins at once
CakePlugin::load('ContactManager'); // Loads a single plugin
loadAll() loads all plugins available, while allowing you to set certain settings for specific plugins. load() works
similarly, but only loads the plugins you explicitly specify.

How To Use Plugins
Before you can use a plugin, you must install and enable it first. See How To Install Plugins.

Plugin Configuration
There is a lot you can do with the load and loadAll methods to help with plugin configuration and routing. Perhaps you
want to load all plugins automatically, while specifying custom routes and bootstrap files for certain plugins.
No problem:
CakePlugin::loadAll(array('Blog' => array('routes' => true),'ContactManager' => array('bootstrap' => true),'WebmasterTools' => array('bootstrap' => true, 'routes' => true),));
With this style of configuration, you no longer need to manually include() or require() a plugin’s configuration or routes
file–It happens automatically at the right time and place. The exact same parameters could have also been supplied to
the load() method, which would have loaded only those three plugins, and not the rest.
Finally, you can also specify a set of defaults for loadAll which will apply to every plugin that doesn’t have a more
specific configuration.
Load the bootstrap file from all plugins, and additionally the routes from the Blog plugin:
CakePlugin::loadAll(array(
array('bootstrap' => true),'Blog' => array('routes' => true)));
Note that all files specified should actually exist in the configured plugin(s) or PHP will give warnings for each file itcannot load. This is especially important to remember when specifying defaults for all plugins.
CakePHP 2.3.0 added an ignoreMissing option, that allows you to ignore any missing routes and bootstrap files whenloading plugins. You can shorten the code needed to load all plugins using this:// Loads all plugins including any possible routes and bootstrap files
CakePlugin::loadAll(array(
array('routes' => true, 'bootstrap' => true, 'ignoreMissing' => true)));

How To Use Plugins

531

CakePHP Cookbook Documentation, Release 2.x

Some plugins additionally need to create one or more tables in your database. In those cases, they will often include aschema file which you can call from the cake shell like this:user@host$ cake schema create --plugin ContactManager
Most plugins will indicate the proper procedure for configuring them and setting up the database in their documentation.
Some plugins will require more setup than others.

Advanced Bootstrapping
If you like to load more than one bootstrap file for a plugin. You can specify an array of files for the bootstrap configuration key:
CakePlugin::loadAll(array('Blog' => array('bootstrap' => array('config1','config2'))));
You can also specify a callable function that needs to be called when the plugin has been loaded:function aCallableFunction($pluginName, $config) {}CakePlugin::loadAll(array('Blog' => array('bootstrap' => 'aCallableFunction')));

Using a Plugin
You can reference a plugin’s controllers, models, components, behaviors, and helpers by prefixing the name of theplugin before the class name.
For example, say you wanted to use the ContactManager plugin’s ContactInfoHelper to output some pretty contact
information in one of your views. In your controller, your $helpers array could look like this:public $helpers = array('ContactManager.ContactInfo');
You would then be able to access the ContactInfoHelper just like any other helper in your view, such as:
echo $this->ContactInfo->address($contact);

532

Chapter 8. Plugins

CakePHP Cookbook Documentation, Release 2.x

How To Create Plugins
As a working example from the How To Use Plugins section, let’s begin to create a ContactManager plugin. To startout, we’ll set up our plugin’s basic directory structure. It should look like this:/app/Plugin/ContactManager/Controller/Component/Model/Behavior/View/Helper/Layouts
Note the name of the plugin folder, ‘ContactManager’. It is important that this folder has the same name as the plugin.
Inside the plugin folder, you’ll notice it looks a lot like a CakePHP application, and that’s basically what it is. You don’t
actually have to include any of those folders if you do not use them. Some plugins might only define a Component anda Behavior, and in that case they can completely omit the ‘View’ directory.
A plugin can also have basically any of the other directories that your application can, such as Config, Console, Lib,
webroot, etc.
Note: If you want to be able to access your plugin with a URL, defining an AppController and AppModel for the plugin
is required. These two special classes are named after the plugin, and extend the parent application’s AppController
and AppModel. Here’s what they should look like for our ContactManager example:// In /app/Plugin/ContactManager/Controller/ContactManagerAppController.php
class ContactManagerAppController extends AppController {}// In /app/Plugin/ContactManager/Model/ContactManagerAppModel.php
class ContactManagerAppModel extends AppModel {}If you forgot to define these special classes, CakePHP will hand you “Missing Controller” errors until you’ve done so.
Please note that the process of creating plugins can be greatly simplified by using the Cake shell.
In order to bake a plugin please use the following command:
user@host$ cake bake plugin ContactManager
Now you can bake using the same conventions which apply to the rest of your app. For example - baking controllers:
user@host$ cake bake controller Contacts --plugin ContactManager
Please refer to the chapter Code Generation with Bake if you have any problems with using the command line.

How To Create Plugins

533

CakePHP Cookbook Documentation, Release 2.x

Warning: Plugins do not work as namespacing to separate code. Due to PHP lacking namespaces in older versions
you cannot have the same class, or same filename, in your plugins. Even if it is two different plugins. So use uniqueclasses and filenames, possible prefixing the class and filename with the plugin name.

Plugin Controllers
Controllers for our ContactManager plugin will be stored in /app/Plugin/ContactManager/Controller/. Since the mainthing we’ll be doing is managing contacts, we’ll need a ContactsController for this plugin.
So, we place our new ContactsController in /app/Plugin/ContactManager/Controller and it looks like so:// In app/Plugin/ContactManager/Controller/ContactsController.php
class ContactsController extends ContactManagerAppController {public $uses = array('ContactManager.Contact');
public function index() {// ...
}}

Note: This controller extends the plugin’s AppController (called ContactManagerAppController) rather than the parent
application’s AppController.
Also note how the name of the model is prefixed with the name of the plugin. This is required to differentiate between
models in the plugin and models in the main application.
In this case, the $uses array would not be required as ContactManager.Contact would be the default model for thiscontroller, however it is included to demonstrate how to properly prepend the plugin name.
If you want to access what we’ve got going thus far, visit /contact_manager/contacts. You should get a “Missing Model”
error because we don’t have a Contact model defined yet.

Plugin Models
Models for the plugin are stored in /app/Plugin/ContactManager/Model. We’ve already defined a ContactsController
for this plugin, so let’s create the model for that controller, called Contact:// In /app/Plugin/ContactManager/Model/Contact.php
class Contact extends ContactManagerAppModel {}Visiting /contact_manager/contacts now (given you’ve got a table in your database called ‘contacts’) should give us a“Missing View” error. Let’s create that next.
Note: If you need to reference a model within your plugin, you need to include the plugin name with the model name,separated with a dot.
For example:

534

Chapter 8. Plugins

CakePHP Cookbook Documentation, Release 2.x// In /app/Plugin/ContactManager/Model/Contact.php
class Contact extends ContactManagerAppModel {public $hasMany = array('ContactManager.AltName');}If you would prefer that the array keys for the association not have the plugin prefix on them, use the alternative syntax:// In /app/Plugin/ContactManager/Model/Contact.php
class Contact extends ContactManagerAppModel {public $hasMany = array('AltName' => array('className' => 'ContactManager.AltName'));}

Plugin Views
Views behave exactly as they do in normal applications. Just place them in the right folder inside of the/app/Plugin/[PluginName]/View/ folder. For our ContactManager plugin, we’ll need a view for our ContactsController::index() action, so let’s include that as well:<!-- /app/Plugin/ContactManager/View/Contacts/index.ctp: --><h1>Contacts</h1><p>Following is a sortable list of your contacts</p><!-- A sortable list of contacts would go here....-->

Note: For information on how to use elements from a plugin, look up Elements

Overriding Plugin Views From Inside Your Application
You can override any plugin views from inside your app using special paths. If you have a plugin called ‘ContactManager’ you can override the view files of the plugin with more application specific view logic by creating files using thefollowing template “app/View/Plugin/[Plugin]/[Controller]/[view].ctp”. For the Contacts controller you could makethe following file:/app/View/Plugin/ContactManager/Contacts/index.ctp
Creating this file, would allow you to override “/app/Plugin/ContactManager/View/Contacts/index.ctp”.

How To Create Plugins

535

CakePHP Cookbook Documentation, Release 2.x

Plugin Assets
A plugin’s web assets (but not PHP files) can be served through the plugin’s ‘webroot’ directory, just like the main
application’s assets:
app/Plugin/ContactManager/webroot/
css/
js/
img/
flash/
pdf/
You may put any type of file in any directory, just like a regular webroot.
But keep in mind that handling static assets, such as images, Javascript and CSS files of plugins, through the Dispatcher
is incredibly inefficient. It is strongly recommended to symlink them for production. For example like this:ln -s app/Plugin/YourPlugin/webroot/css/yourplugin.css app/webroot/css/yourplugin.css

Linking to Assets in Plugins
Simply prepend /plugin_name/ to the beginning of a request for an asset within that plugin, and it will work as if theasset were in your application’s webroot.
For
example,linking
to‘/contact_manager/js/some_file.js’‘app/Plugin/ContactManager/webroot/js/some_file.js’.

would

serve

the

asset

Note: It is important to note the /your_plugin/ prefix before the asset path. That makes the magic happen!
Changed in version 2.1: Use plugin syntax to request assets. For example in your View:<?php echo $this->Html->css("ContactManager.style"); ?>Components, Helpers and Behaviors
A plugin can have Components, Helpers and Behaviors just like a regular CakePHP application. You can even create
plugins that consist only of Components, Helpers or Behaviors which can be a great way to build reusable componentsthat can easily be dropped into any project.
Building these components is exactly the same as building it within a regular application, with no special naming
convention.
Referring to your component from inside or outside of your plugin requires only that you prefix the plugin name before
the name of the component. For example:// Component defined in 'ContactManager' pluginclass ExampleComponent extends Component {}// Within your controllers:public $components = array('ContactManager.Example');536

Chapter 8. Plugins

CakePHP Cookbook Documentation, Release 2.x

The same technique applies to Helpers and Behaviors.
Note: When creating Helpers you may find AppHelper is not automatically available. You should declare the resources
you need with Uses:// Declare use of AppHelper for your Plugin's Helper
App::uses('AppHelper', 'View/Helper');

Expand Your Plugin
This example created a good start for a plugin, but there is a lot more that you can do. As a general rule, anything you
can do with your application, you can do inside of a plugin instead.
Go ahead, include some third-party libraries in ‘Vendor’, add some new shells to the cake console, and don’t forget tocreate test cases so your plugin users can automatically test your plugin’s functionality!
In our ContactManager example, we might create add/remove/edit/delete actions in the ContactsController, implement
validation in the Contact model, and implement the functionality one might expect when managing their contacts. It’s
up to you to decide what to implement in your plugins. Just don’t forget to share your code with the community so thateveryone can benefit from your awesome, reusable components!

Plugin Tips
Once a plugin has been installed in /app/Plugin/, you can access it at the URL /plugin_name/controller_name/action.
In our ContactManager plugin example, we’d access our ContactsController at /contact_manager/contacts.
Some final tips on working with plugins in your CakePHP applications:• When you don’t have a [Plugin]AppController and [Plugin]AppModel, you’ll get missing Controller errors whentrying to access a plugin controller.
• You can define your own layouts for plugins, inside app/Plugin/[Plugin]/View/Layouts. Otherwise, plugins will
use the layouts from the /app/View/Layouts folder by default.
• You can do inter-plugin communication bycontroller_name/action'); in your controllers.

using$this->requestAction('/plugin_name/• If you use requestAction, make sure controller and model names are as unique as possible. Otherwise you might
get PHP “redefined class . . . ” errors.
• When adding routes with extensions to your plugin, ensure you use Router::setExtensions() so you do not
override application routing.

How To Create Plugins

537

CakePHP Cookbook Documentation, Release 2.x

Publish Your Plugin
You can add your plugin to plugins.cakephp.org104 or propose it to the awesome-cakephp list105 .
Also, you might want to create a composer.json file and publish your plugin at packagist.org106 . This way it can easily
be used through Composer.
Choose a semantically meaningful name for the package name. This should ideally be prefixed with the dependency,
in this case “cakephp” as the framework. The vendor name will usually be your GitHub username. Do not use the
CakePHP namespace (cakephp) as this is reserved to CakePHP owned plugins. The convention is to use lowercase
letters and dashes as separator.
So if you created a plugin “Logging” with your GitHub account “FooBar”, a good name would be foo-bar/cakephplogging. And the CakePHP owned “Localized” plugin can be found under cakephp/localized respectively.

104 https://plugins.cakephp.org

105 https://github.com/FriendsOfCake/awesome-cakephp
106 https://packagist.org/

538

Chapter 8. Plugins

CHAPTER 9

Shells, Tasks & Console Tools

CakePHP features not only a web framework but also a console framework for creating console applications. Console
applications are ideal for handling a variety of background tasks such as maintenance, and completing work outsideof the request-response cycle. CakePHP console applications allow you to reuse your application classes from thecommand line.
CakePHP comes with a number of console applications out of the box. Some of these applications are used in concert
with other CakePHP features (like ACL or i18n), and others are for general use in getting you working faster.

The CakePHP console
This section provides an introduction into CakePHP at the command-line. If you’ve ever needed access to your
CakePHP MVC classes in a cron job or other command-line script, this section is for you.
PHP provides a CLI client that makes interfacing with your file system and applications much smoother. The CakePHP
console provides a framework for creating shell scripts. The Console uses a dispatcher-type setup to load a shell ortask, and provide its parameters.
Note: A command-line (CLI) build of PHP must be available on the system if you plan to use the Console.
Before we get into specifics, let’s make sure we can run the CakePHP console. First, you’ll need to bring up a systemshell. The examples shown in this section will be in bash, but the CakePHP Console is Windows-compatible as well.
Let’s execute the Console program from bash. This example assumes that the user is currently logged into a bash
prompt and is currently at the root of a CakePHP application.
CakePHP applications contain a Console directory that contains all the shells and tasks for an application. It alsocomes with an executable:$ cd /path/to/cakephp/app$ Console/cake539

CakePHP Cookbook Documentation, Release 2.x

It’s often wise to add the core cake executable to your system path so you can use the cake command anywhere. This
comes in handy when you are creating new projects. See Adding cake to your system path for how to make cakeavailable systemwide.
Running the Console with no arguments produces this help message:
Welcome to CakePHP v2.0.0 Console--------------------------------------------------------------App : app
Path: /path/to/cakephp/app/--------------------------------------------------------------Current Paths:-app: app-working: /path/to/cakephp/app-root: /path/to/cakephp/-core: /path/to/cakephp/core
Changing Paths:
your working path should be the same as your application path
to change your path use the '-app' param.
Example: -app relative/path/to/cakephp/app or -app /absolute/path/to/cakephp/app
Available Shells:
acl [CORE]
api [CORE]
bake [CORE]
command_list [CORE]
console [CORE]

i18n [CORE]
import [app]schema [CORE]
testsuite [CORE]
upgrade [CORE]

To run a command, type 'cake shell_name [args]'
To get help on a specific command, type 'cake shell_name help'
The first information printed relates to paths. This is especially helpful if you’re running the console from different
parts of the filesystem.
Since many users add the CakePHP console to their system’s path so it can be accessed easily. Printing out the working,root, app, and core paths allows you to see where the console will be making changes. To change the app folder you
wish to work with, you can supply its path as the first argument to the cake command. This next example shows how
to specify an app folder, assuming you’ve already added the console folder to your PATH:$ cake -app /path/to/cakephp/app
The path supplied can be relative to the current working directory or supplied as an absolute path.

540

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

Adding cake to your system path
If you are on a *nix system (linux, MacOSX) the following steps will let you add the cake executable to your systempath.
1. Locate where your CakePHP install, and cake executable are. For example /Users/mark/cakephp/lib/Cake/
Console/cake2. Edit your .bashrc or .bash_profile file in your home directory, and add the following:
export PATH="$PATH:/Users/mark/cakephp/lib/Cake/Console"
3. Reload the bash configuration or open a new terminal, and cake should work anywhere.
If you are on Windows Vista or 7, you should follow the steps below.
1. Locate where your CakePHP install and cake executable are. For example C:\xampp\htdocs\cakephp\lib\
Cake\Console
2. Open System Properties window from My Computer. You want to try the shortcut Windows Key + Pause or
Windows Key + Break. Or, from the Desktop, right-click My Computer, click Properties then click Advanced
System Settings link in the left column
3. Go under Advanced tab and click on Environment Variables button
4. In the System Variables portion, reach Path variable and double-click on it to Edit
5. Add the cake install path string followed by a semi colon. Result example:%SystemRoot%\system32;%SystemRoot%;C:\xampp\htdocs\cakephp\lib\Cake\Console;
6. Click Ok and cake should work anywhere.

Creating a shell
Let’s create a shell for use in the Console. For this example, we’ll create a simple Hello world shell. In your applications
Console/Command directory create HelloShell.php. Put the following code inside it:class HelloShell extends AppShell {public function main() {$this->out('Hello world.');}}The conventions for shell classes are that the class name should match the file name, with the suffix of Shell. In ourshell we created a main() method. This method is called when a shell is called with no additional commands. We’ll
add some more commands in a bit, but for now let’s just run our shell. From your application directory, run:Console/cake hello
You should see the following output:
Welcome to CakePHP v2.0.0 Console--------------------------------------------------------------App : app
Path: /Users/markstory/Sites/cake_dev/app/(continues on next page)

Creating a shell541

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)--------------------------------------------------------------Hello world.
As mentioned before, the main() method in shells is a special method called whenever there are no other commands
or arguments given to a shell. You may have also noticed that HelloShell is extending AppShell. Much like The App
Controller, AppShell gives you a base class to contain all your common functions or logic. You can define an AppShell,by creating app/Console/Command/AppShell.php. If you don’t have one, CakePHP will use the built-in one. Since
our main method wasn’t very interesting let’s add another command that does something:class HelloShell extends AppShell {public function main() {$this->out('Hello world.');}public function hey_there() {$this->out('Hey there ' . $this->args[0]);}}After saving this file you should be able to run Console/cake hello hey_there your-name and see your nameprinted out. Any public method not prefixed by an _ is allowed to be called from the command line. In our hey_there
method we also used $this->args, this property contains an array of all the positional arguments provided to acommand. You can also use switches or options on shell applications, these are available at $this->params, andthrough the param() method. We’ll cover that in a bit.
When using a main() method you won’t be able to use the positional arguments or parameters. This is because thefirst positional argument or option is interpreted as the command name. If you want to use arguments and options, you
should use method names other than main.

Using Models in your shells
You’ll often need access to your application’s business logic in shell utilities; CakePHP makes that super easy. By
setting a $uses property, you can define an array of models you want to have access to in your shell. The definedmodels are loaded in as properties attached to your shell, just like a controller gets models attached to it:class UserShell extends AppShell {public $uses = array('User');
public function show() {$user = $this->User->findByUsername($this->args[0]);$this->out(print_r($user, true));}}
The above shell, will fetch a user by username and display the information stored in the database.

542

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

Shell tasks
There will be times when building more advanced console applications, you’ll want to compose functionality into reusable classes that can be shared across many shells. Tasks allow you to extract commands into classes. For example
the bake is made almost entirely of tasks. You define a shell’s tasks by using the $tasks property:
class UserShell extends AppShell {public $tasks = array('Template');}You can use tasks from plugins using the standard plugin syntax. Tasks are stored in Console/Command/Task/ in files
named after their classes. So if we were to create a new ‘FileGenerator’ task, you would create Console/Command/
Task/FileGeneratorTask.php.
Each task must at least implement an execute() method. The ShellDispatcher, will call this method when the task isinvoked. A task class looks like:class FileGeneratorTask extends Shell {public $uses = array('User');
public function execute() {}}A shell can also access its tasks as properties, which makes tasks great for making re-usable chunks of functionality
similar to Components:// found in Console/Command/SeaShell.php
class SeaShell extends AppShell {public $tasks = array('Sound'); // found in Console/Command/Task/SoundTask.php
public function main() {$this->Sound->execute();}}You can also access tasks directly from the command line:$ cake sea soundNote: In order to access tasks directly from the command line, the task must be included in the shell class’ $tasks
property. Therefore, be warned that a method called “sound” in the SeaShell class would override the ability to accessthe functionality in the Sound task specified in the $tasks array.

Shell tasks

543

CakePHP Cookbook Documentation, Release 2.x

Loading tasks on the fly with TaskCollection
You can load tasks on the fly using the Task collection object. You can load tasks that were not declared in $tasks thisway:
$Project = $this->Tasks->load('Project');
Would load and return a ProjectTask instance. You can load tasks from plugins using:$ProgressBar = $this->Tasks->load('ProgressBar.ProgressBar');Invoking other shells from your shell
Shells no longer have direct access to the ShellDispatcher any more through $this->Dispatch. There are still manycases where you will want to invoke one shell from another though. Shell::dispatchShell() gives you the ability to callother shells by providing the argv for the sub shell. You can provide arguments and options either as var args or as astring:// As a string$this->dispatchShell('schema create Blog --plugin Blog');// As an array$this->dispatchShell('schema', 'create', 'Blog', '--plugin', 'Blog');
The above shows how you can call the schema shell to create the schema for a plugin from inside your plugin’s shell.

Console output levels
Shells often need different levels of verbosity. When running as cron jobs, most output is un-necessary. And there
are times when you are not interested in everything that a shell has to say. You can use output levels to flag output
appropriately. The user of the shell, can then decide what level of detail they are interested in by setting the correct flag
when calling the shell. Shell::out() supports 3 types of output by default.
• QUIET - Only absolutely important information should be marked for quiet output.
• NORMAL - The default level, and normal usage• VERBOSE - Mark messages that may be too noisy for everyday use, but helpful for debugging as VERBOSE
You can mark output as follows:// would appear at all levels.
$this->out('Quiet message', 1, Shell::QUIET);// would not appear when quiet output is toggled$this->out('normal message', 1, Shell::NORMAL);$this->out('loud message', 1, Shell::VERBOSE);// would only appear when verbose output is enabled.
$this->out('extra message', 1, Shell::VERBOSE);
You can control the output level of shells, by using the --quiet and --verbose options. These options are added bydefault, and allow you to consistently control output levels inside your CakePHP shells.
544

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

Styling output
Styling output is done by including tags - just like HTML - in your output. ConsoleOutput will replace these tags withthe correct ansi code sequence, or remove the tags if you are on a console that doesn’t support ansi codes. There areseveral built-in styles, and you can create more. The built-in ones are• error Error messages. Red underlined text.
• warning Warning messages. Yellow text.
• info Informational messages. Cyan text.
• comment Additional text. Blue text.
• question Text that is a question, added automatically by shell.
You can create additional styles using $this->stdout->styles(). To declare a new output style you could do:$this->stdout->styles('flashy', array('text' => 'magenta', 'blink' => true));
This would then allow you to use a <flashy> tag in your shell output, and if ansi colours are enabled, the following would be rendered as blinking magenta text $this->out('<flashy>Whoooa</flashy> Something went
wrong');. When defining styles you can use the following colours for the text and background attributes:• black• red• green• yellow• blue• magenta• cyan• whiteYou can also use the following options as boolean switches, setting them to a truthy value enables them.
• bold• underline• blink• reverse
Adding a style makes it available on all instances of ConsoleOutput as well, so you don’t have to redeclare styles forboth stdout and stderr objects.

Styling output

545

CakePHP Cookbook Documentation, Release 2.x

Turning off colouring
Although colouring is pretty awesome, there may be times when you want to turn it off, or force it on:$this->stdout->outputAs(ConsoleOutput::RAW);The above will put the output object into raw output mode. In raw output mode, no styling is done at all. There arethree modes you can use.
• ConsoleOutput::RAW - Raw output, no styling or formatting will be done. This is a good mode to use if you
are outputting XML or, want to debug why your styling isn’t working.
• ConsoleOutput::PLAIN - Plain text output, known style tags will be stripped from the output.
• ConsoleOutput::COLOR - Output with color escape codes in place.
By default on *nix systems ConsoleOutput objects default to colour output. On Windows systems, plain output is thedefault unless the ANSICON environment variable is present.

Configuring options and generating helpclass ConsoleOptionParser
Console option parsing in CakePHP has always been a little bit different from everything else on the command line. In
2.0 ConsoleOptionParser helps provide a more familiar command line option and argument parser.
OptionParsers allow you to accomplish two goals at the same time. First they allow you to define the options andarguments, separating basic input validation and your code. Secondly, it allows you to provide documentation, that isused to generate well formatted help file.
The console framework gets your shell’s option parser by calling $this->getOptionParser(). Overriding thismethod allows you to configure the OptionParser to match the expected inputs of your shell. You can also configure
subcommand option parsers, which allow you to have different option parsers for subcommands and tasks. The ConsoleOptionParser implements a fluent interface and includes methods for easily setting multiple options/arguments atonce.
public function getOptionParser() {$parser = parent::getOptionParser();//configure parser
return $parser;}

Configuring an option parser with the fluent interface
All of the methods that configure an option parser can be chained, allowing you to define an entire option parser in one
series of method calls:
public function getOptionParser() {$parser = parent::getOptionParser();$parser->addArgument('type', array('help' => 'Either a full path or type of class.'))->addArgument('className', array('help' => 'A CakePHP core class name (e.g: Component, HtmlHelper).'))->addOption('method', array((continues on next page)

546

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'short' => 'm','help' => __('The specific method you want help on.')))->description(__('Lookup doc block comments for classes in CakePHP.'));
return $parser;}
The methods that allow chaining are:• description()• epilog()• command()• addArgument()• addArguments()• addOption()• addOptions()• addSubcommand()• addSubcommands()
ConsoleOptionParser::description($text = null)Gets or sets the description for the option parser. The description displays above the argument and option information.
By passing in either an array or a string, you can set the value of the description. Calling with no arguments will returnthe current value:// Set multiple lines at once$parser->description(array('line one', 'line two'));// read the current value$parser->description();ConsoleOptionParser::epilog($text = null)Gets or sets the epilog for the option parser. The epilog is displayed after the argument and option information. By
passing in either an array or a string, you can set the value of the epilog. Calling with no arguments will return thecurrent value:// Set multiple lines at once$parser->epilog(array('line one', 'line two'));// read the current value$parser->epilog();

Configuring options and generating help547

CakePHP Cookbook Documentation, Release 2.x

Adding arguments
ConsoleOptionParser::addArgument($name, $params = array())
Positional arguments are frequently used in command line tools, and ConsoleOptionParser allows you todefine positional arguments as well as make them required. You can add arguments one at a time with$parser->addArgument(); or multiple at once with $parser->addArguments();:$parser->addArgument('model', array('help' => 'The model to bake'));
You can use the following options when creating an argument:• help The help text to display for this argument.
• required Whether this parameter is required.
• index The index for the arg, if left undefined the argument will be put
onto the end of the arguments. If you define the same index twice the first option will be overwritten.
• choices An array of valid choices for this argument. If left empty allvalues are valid. An exception will be raised when parse() encounters an invalid value.
Arguments that have been marked as required will throw an exception when parsing the command if they have been
omitted. So you don’t have to handle that in your shell.
ConsoleOptionParser::addArguments(array $args)
If you have an array with multiple arguments you can use $parser->addArguments() to add multiple arguments atonce.
$parser->addArguments(array('node' => array('help' => 'The node to create', 'required' => true),'parent' => array('help' => 'The parent node', 'required' => true)));
As with all the builder methods on ConsoleOptionParser, addArguments can be used as part of a fluent method chain.

Validating arguments
When creating positional arguments, you can use the required flag, to indicate that an argument must be present whena shell is called. Additionally you can use choices to force an argument to be from a list of valid choices:$parser->addArgument('type', array('help' => 'The type of node to interact with.','required' => true,'choices' => array('aro', 'aco')));
The above will create an argument that is required and has validation on the input. If the argument is either missing,or has an incorrect value an exception will be raised and the shell will be stopped.

548

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

Adding Options
ConsoleOptionParser::addOption($name, $options = array())
Options or flags are also frequently used in command line tools. ConsoleOptionParser supports creating options
with both verbose and short aliases, supplying defaults and creating boolean switches. Options are created with either$parser->addOption() or $parser->addOptions().
$parser->addOption('connection', array('short' => 'c','help' => 'connection','default' => 'default',));
The above would allow you to use either cake myshell --connection=other, cake myshell --connection
other, or cake myshell -c other when invoking the shell. You can also create boolean switches, these switches
do not consume values, and their presence just enables them in the parsed parameters.
$parser->addOption('no-commit', array('boolean' => true));With this option, when calling a shell like cake myshell --no-commit something the no-commit param would
have a value of true, and ‘something’ would be a treated as a positional argument. The built-in --help, --verbose,and --quiet options use this feature.
When creating options you can use the following options to define the behavior of the option:• short - The single letter variant for this option, leave undefined for none.
• help - Help text for this option. Used when generating help for the option.
• default - The default value for this option. If not defined the default will be true.
• boolean - The option uses no value, it’s just a boolean switch. Defaults to false.
• choices An array of valid choices for this option. If left empty all values are valid. An exception will be raised
when parse() encounters an invalid value.
ConsoleOptionParser::addOptions(array $options)If you have an array with multiple options you can use $parser->addOptions() to add multiple options at once.
$parser->addOptions(array('node' => array('short' => 'n', 'help' => 'The node to create'),'parent' => array('short' => 'p', 'help' => 'The parent node')));
As with all the builder methods on ConsoleOptionParser, addOptions is can be used as part of a fluent method chain.

Validating options
Options can be provided with a set of choices much like positional arguments can be. When an option has defined
choices, those are the only valid choices for an option. All other values will raise an InvalidArgumentException:$parser->addOption('accept', array('help' => 'What version to accept.','choices' => array('working', 'theirs', 'mine')));

Configuring options and generating help549

CakePHP Cookbook Documentation, Release 2.x

Using boolean options
Options can be defined as boolean options, which are useful when you need to create some flag options. Like options
with defaults, boolean options always include themselves into the parsed parameters. When the flags are present they
are set to true, when they are absent false:$parser->addOption('verbose', array('help' => 'Enable verbose output.','boolean' => true));
The following option would result in $this->params['verbose'] always being available. This lets you omit
empty() or isset() checks for boolean flags:
if ($this->params['verbose']) {// do something}// as of 2.7
if ($this->param('verbose')) {// do something}
Since the boolean options are always defined as true or false you can omit additional check methods.

Adding subcommands
ConsoleOptionParser::addSubcommand($name, $options = array())
Console applications are often made of subcommands, and these subcommands may require special option parsing andhave their own help. A perfect example of this is bake. Bake is made of many separate tasks that all have their own
help and options. ConsoleOptionParser allows you to define subcommands and provide command specific option
parsers so the shell knows how to parse commands for its tasks:$parser->addSubcommand('model', array('help' => 'Bake a model','parser' => $this->Model->getOptionParser()));
The above is an example of how you could provide help and a specialized option parser for a shell’s task. By calling
the Task’s getOptionParser() we don’t have to duplicate the option parser generation, or mix concerns in our shell.
Adding subcommands in this way has two advantages. First it lets your shell easily document its subcommands in thegenerated help, and it also allows easy access to the subcommand help. With the above subcommand created you could
call cake myshell --help and see the list of subcommands, and also run cake myshell model --help to viewthe help for just the model task.
When defining a subcommand you can use the following options:• help - Help text for the subcommand.
• parser - A ConsoleOptionParser for the subcommand. This allows you to create method specific option parsers.
When help is generated for a subcommand, if a parser is present it will be used. You can also supply the parser
as an array that is compatible with ConsoleOptionParser::buildFromArray()Adding subcommands can be done as part of a fluent method chain.

550

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

Building a ConsoleOptionParser from an array
ConsoleOptionParser::buildFromArray($spec)
As previously mentioned, when creating subcommand option parsers, you can define the parser spec as an array forthat method. This can help make building subcommand parsers easier, as everything is an array:$parser->addSubcommand('check', array('help' => __('Check the permissions between an ACO and ARO.'),'parser' => array('description' => array(__("Use this command to grant ACL permissions. Once executed, the "),__("ARO specified (and its children, if any) will have ALLOW access "),__("to the specified ACO action (and the ACO's children, if any).")),'arguments' => array('aro' => array('help' => __('ARO to check.'), 'required' => true),'aco' => array('help' => __('ACO to check.'), 'required' => true),'action' => array('help' => __('Action to check'))))));
Inside the parser spec, you can define keys for arguments, options, description and epilog. You cannot define
subcommands inside an array style builder. The values for arguments, and options, should follow the format that
ConsoleOptionParser::addArguments() and ConsoleOptionParser::addOptions() use. You can also use
buildFromArray on its own, to build an option parser:
public function getOptionParser() {return ConsoleOptionParser::buildFromArray(array('description' => array(__("Use this command to grant ACL permissions. Once executed, the "),__("ARO specified (and its children, if any) will have ALLOW access "),__("to the specified ACO action (and the ACO's children, if any).")),'arguments' => array('aro' => array('help' => __('ARO to check.'), 'required' => true),'aco' => array('help' => __('ACO to check.'), 'required' => true),'action' => array('help' => __('Action to check')))));}

Getting help from shells
With the addition of ConsoleOptionParser getting help from shells is done in a consistent and uniform way. By using
the --help or -h option you can view the help for any core shell, and any shell that implements a ConsoleOptionParser:
cake bake --help
cake bake -hWould both generate the help for bake. If the shell supports subcommands you can get help for those in a similar
fashion:Configuring options and generating help551

CakePHP Cookbook Documentation, Release 2.x

cake bake model --help
cake bake model -hThis would get you the help specific to bake’s model task.

Getting help as XML
When building automated tools or development tools that need to interact with CakePHP shells, it’s nice to have help
available in a machine parse-able format. The ConsoleOptionParser can provide help in xml by setting an additional
argument:cake bake --help xml
cake bake -h xml
The above would return an XML document with the generated help, options, arguments and subcommands for theselected shell. A sample XML document would look like:<?xml version="1.0"?><shell><command>bake fixture</command><description>Generate fixtures for use with the test suite. You can use`bake fixture all` to bake all fixtures.</description><epilog>
Omitting all arguments and options will enter into an interactivemode.
</epilog><subcommands/><options><option name="--help" short="-h" boolean="1"><default/><choices/></option><option name="--verbose" short="-v" boolean="1"><default/><choices/></option><option name="--quiet" short="-q" boolean="1"><default/><choices/></option><option name="--count" short="-n" boolean=""><default>10</default><choices/></option><option name="--connection" short="-c" boolean=""><default>default</default><choices/></option><option name="--plugin" short="-p" boolean=""><default/><choices/></option>(continues on next page)

552

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)<option name="--records" short="-r" boolean="1"><default/><choices/></option></options><arguments><argument name="name" help="Name of the fixture to bake.
Can use Plugin.name to bake plugin fixtures." required=""><choices/></argument></arguments></shell>Routing in shells / CLI
In command-line interface (CLI), specifically your shells and tasks, env('HTTP_HOST') and other webbrowser specificenvironment variables are not set.
If you generate reports or send emails that make use of Router::url() those will contain the default host http://
localhost/ and thus resulting in invalid URLs. In this case you need to specify the domain manually. You can dothat using the Configure value App.fullBaseUrl from your bootstrap or config, for example.
For sending emails, you should provide CakeEmail class with the host you want to send the email with:$Email = new CakeEmail();$Email->domain('www.example.org');
This asserts that the generated message IDs are valid and fit to the domain the emails are sent from.

Shell API
class AppShell
AppShell can be used as a base class for all your shells. It should extend Shell, and be located in Console/
Command/AppShell.php
class Shell($stdout = null, $stderr = null, $stdin = null)
Shell is the base class for all shells, and provides a number of functions for interacting with user input, outputting
text a generating errors.
property Shell::$tasks
An array of tasks you want loaded for this shell/task.
property Shell::$uses
An array of models that should be loaded for this shell/task.
Shell::clear()
Clears the current output being displayed.
Shell::param($name)Get the value of an option/parameter. Will return null if the parameter does not exist.
New in version 2.7.
Routing in shells / CLI553

CakePHP Cookbook Documentation, Release 2.x

Shell::createFile($path, $contents)
Parameters• $path (string) – Absolute path to the file you want to create.
• $contents (string) – Contents to put in the file.
Creates a file at a given path. If the Shell is interactive, a warning will be generated, and the user asked if theywant to overwrite the file if it already exists. If the shell’s interactive property is false, no question will be asked
and the file will simply be overwritten.
Shell::dispatchShell()
Dispatch a command to another Shell. Similar to Controller::requestAction() but intended for runningshells from other shells.
See Invoking other shells from your shell.
Shell::err($message = null, $newlines = 1)
Parameters• $method (string) – The message to print.
• $newlines (integer) – The number of newlines to follow the message.
Outputs a method to stderr, works similar to Shell::out()
Shell::error($title, $message = null)Parameters• $title (string) – Title of the error• $message (string) – An optional error message
Displays a formatted error message and exits the application with status code 1
Shell::getOptionParser()Should return a ConsoleOptionParser object, with any sub-parsers for the shell.
Shell::hasMethod($name)
Check to see if this shell has a callable method by the given name.
Shell::hasTask($task)
Check to see if this shell has a task with the provided name.
Shell::hr($newlines = 0, $width = 63)
Parameters• $newlines (int) – The number of newlines to precede and follow the line.
• $width (int) – The width of the line to draw.
Create a horizontal line preceded and followed by a number of newlines.
Shell::in($prompt, $options = null, $default = null)Parameters• $prompt (string) – The prompt to display to the user.
• $options (array) – An array of valid choices the user can pick from. Picking an invalid
option will force the user to choose again.

554

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x• $default (string) – The default option if there is one.
This method helps you interact with the user, and create interactive shells. It will return the users answer to theprompt, and allows you to provide a list of valid options the user can choose from:$selection = $this->in('Red or Green?', array('R', 'G'), 'R');
The selection validation is case-insensitive.
Shell::initialize()
Initializes the Shell acts as constructor for subclasses allows configuration of tasks prior to shell execution.
Shell::loadTasks()
Loads tasks defined in public Shell::$tasks
Shell::nl($multiplier = 1)
Parameters• $multiplier (int) – Number of times the linefeed sequence should be repeated
Returns a number of linefeed sequences.
Shell::out($message = null, $newlines = 1, $level = Shell::NORMAL)
Parameters• $message (string) – The message to print.
• $newlines (integer) – The number of newlines to follow the message.
• $level (integer) – The highest Console output levels this message should display at.
The primary method for generating output to the user. By using levels, you can limit how verbose a shell is.
out() also allows you to use colour formatting tags, which will enable coloured output on systems that support it.
There are several built-in styles for colouring text, and you can define your own.
• error Error messages.
• warning Warning messages.
• info Informational messages.
• comment Additional text.
• question Magenta text used for user prompts
By formatting messages with style tags you can display styled output:$this->out('<warning>This will remove data from the filesystems.</warning>');
By default on *nix systems ConsoleOutput objects default to colour output. On Windows systems, plain output
is the default unless the ANSICON environment variable is present.
Shell::overwrite($message = null, $newlines = 1, $size = null)Parameters• $message (string) – The message to print.
• $newlines (integer) – The number of newlines to follow the message.
• $size (integer) – The number of bytes to overwrite

Shell API

555

CakePHP Cookbook Documentation, Release 2.x

A useful method to generate progress bars or to avoid outputting too many lines.
Warning: You cannot overwrite text that contains newlines.
New in version 2.6.
Shell::runCommand($command, $argv)
Runs the Shell with the provided argv.
Delegates calls to Tasks and resolves methods inside the class. Commands are looked up with the following
order:• Method on the shell.
• Matching task name.
• main() method.
If a shell implements a main() method, all missing method calls will be sent to main() with the original methodname in the argv.
Shell::shortPath($file)
Makes absolute file path easier to read.
Shell::startup()
Starts up the Shell and displays the welcome message. Allows for checking and configuring prior to commandor main execution.
Override this method if you want to remove the welcome information, or otherwise modify the pre-commandflow.
Shell::wrapText($text, $options = array())
Wrap a block of text. Allows you to set the width, and indenting on a block of text.
Parameters• $text (string) – The text to format• $options (array) –• width The width to wrap to. Defaults to 72• wordWrap Only wrap on words breaks (spaces) Defaults to true.
• indent Indent the text with the string provided. Defaults to null.

More topics
Shell Helpers
New in version 2.8: Shell Helpers were added in 2.8.0
Shell Helpers let you package up complex output generation code. Shell Helpers can be accessed and used from any
shell or task:// Output some data as a table.
$this->helper('table')->output($data);// Get a helper from a plugin.
$this->helper('Plugin.HelperName')->output($data);

556

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

You can also get instances of helpers and call any public methods on them:// Get and use the Progress Helper.
$progress = $this->helper('Progress');$progress->increment(10);$progress->draw();

Creating Helpers
While CakePHP comes with a few shell helpers you can create more in your application or plugins. As an example,
we’ll create a simple helper to generate fancy headings. First create the app/Console/Helper/HeadingHelper.php andput the following in it:<?php
App::uses("BaseShellHelper", "Console/Helper");class HeadingHelper extends BaseShellHelper{
public function output($args){$args += array('', '#', 3);$marker = str_repeat($args[1], $args[2]);$this->_consoleOutput->out($marker . ' ' . $args[0] . ' ' . $marker);}}
We can then use this new helper in one of our shell commands by calling it:// With ### on either side
$this->helper('heading')->output('It works!');// With ~~~~ on either side
$this->helper('heading')->output('It works!', '~', 4);

Built-In Helpers
Table Helper
The TableHelper assists in making well formatted ASCII art tables. Using it is pretty simple:$data = array(
array('Header 1', 'Header', 'Long Header'),array('short', 'Longish thing', 'short'),array('Longer thing', 'short', 'Longest Value'),);$this->helper('table')->output($data);// Outputs+--------------+---------------+---------------+| Header 1| Header| Long Header|+--------------+---------------+---------------+(continues on next page)

More topics

557

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)| short| Longish thing | short|| Longer thing | short| Longest Value |+--------------+---------------+---------------+

Progress Helper
The ProgressHelper can be used in two different ways. The simple mode lets you provide a callback that is invokeduntil the progress is complete:$this->helper('progress')->output(function ($progress) {// Do work here.
$progress->increment(20);});
You can control the progress bar more by providing additional options:• total The total number of items in the progress bar. Defaults to 100.
• width The width of the progress bar. Defaults to 80.
• callback The callback that will be called in a loop to advance the progress bar.
An example of all the options in use would be:$this->helper('progress')->output(array('total' => 10,'width' => 20,'callback' => function ($progress) {$progress->increment(2);}));
The progress helper can also be used manually to increment and re-render the progress bar as necessary:$progress = $this->helper('Progress');$progress->init(array('total' => 10,'width' => 20,));$progress->increment(4);$progress->draw();

Running Shells as cronjobs
A common thing to do with a shell is making it run as a cronjob to clean up the database once in a while or send
newsletters. This is trivial to setup, for example:
*/5
# *
#
#

*
*

*
*

*
*

*
*

cd /full/path/to/app && Console/cake myshell myparamcommand to execute(continues on next page)

558

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

#
# |
#
#
#
# \

\
|

|

|
\

\
\

day of week (0 - 6) (0 to 6 are Sunday to Saturday,
or use names)
month (1 - 12)
day of month (1 - 31)
hour (0 - 23)
min (0 - 59)

You can see more info here: https://en.wikipedia.org/wiki/Cron

Completion Shell
New in version 2.5.
Working with the console gives the developer a lot of possibilities but having to completely know and write thosecommands can be tedious. Especially when developing new shells where the commands differ per minute iteration.
The Completion Shells aids in this matter by providing an API to write completion scripts for shells like bash, zsh, fishetc.
Sub Commands
The Completion Shell consists of a number of sub commands to assist the developer creating it’s completion script.
Each for a different step in the autocompletion process.
commands
For the first step commands outputs the available Shell Commands, including plugin name when applicable. (Allreturned possibilities, for this and the other sub commands, are separated by a space.) For example:./Console/cake Completion commands
Returns:
acl api bake command_list completion console i18n schema server test testsuite upgradeYour completion script can select the relevant commands from that list to continue with. (For this and the following
sub commands.)
subCommands
Once the preferred command has been chosen subCommands comes in as the second step and outputs the possible subcommand for the given shell command. For example:./Console/cake Completion subcommands bake
Returns:
controller db_config fixture model plugin project test view

More topics

559

CakePHP Cookbook Documentation, Release 2.x

options
As the third and final options outputs options for the given (sub) command as set in getOptionParser. (Including thedefault options inherited from Shell.) For example:./Console/cake Completion options bake
Returns:--help -h --verbose -v --quiet -q --connection -c --theme -t

Bash Example
The following bash example comes from the original author:# bash completion for CakePHP console_cake(){
local cur prev opts cake
COMPREPLY=()
cake="${COMP_WORDS[0]}"cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
if [[ "$cur" == -* ]] ; thenif [[ ${COMP_CWORD} = 1 ]] ; then
opts=$(${cake} Completion options)elif [[ ${COMP_CWORD} = 2 ]] ; then
opts=$(${cake} Completion options "${COMP_WORDS[1]}")else
opts=$(${cake} Completion options "${COMP_WORDS[1]}" "${COMP_WORDS[2]}")fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )return 0fi
if [[ ${COMP_CWORD} = 1 ]] ; then
opts=$(${cake} Completion commands)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )return 0fi
if [[ ${COMP_CWORD} = 2 ]] ; then
opts=$(${cake} Completion subcommands $prev)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )if [[ $COMPREPLY = "" ]] ; then
COMPREPLY=( $(compgen -df -- ${cur}) )return 0fi
return 0(continues on next page)

560

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)fi

opts=$(${cake} Completion fuzzy "${COMP_WORDS[@]:1}")
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )if [[ $COMPREPLY = "" ]] ; then
COMPREPLY=( $(compgen -df -- ${cur}) )return 0fi
return 0;}
complete -F _cake cake Console/cake

Code Generation with Bake
CakePHP’s Bake console is another effort to get you up and running in CakePHP – fast. The Bake console can create
any of CakePHP’s basic ingredients: models, views and controllers. And we aren’t just talking skeleton classes: Bake
can create a fully functional application in just a few minutes. In fact, Bake is a natural step to take once an application
has been scaffolded.
See The CakePHP console section for instructions on how to use the CakePHP console in general. Depending on theconfiguration of your setup, you may have to set execute rights on the cake bash script or call it using ./Console/cakebake. The cake console is run using the PHP CLI (command line interface). If you have problems running the script,
ensure that you have the PHP CLI installed and that it has the proper modules enabled (eg: MySQL) Users also might
have issues if the database host is ‘localhost’ and should try ‘127.0.0.1’ instead. This could cause issues with PHP CLI.
When running Bake for the first time, you’ll be prompted to create a Database Configuration file, if you haven’t created
one already.
After you’ve created a Database Configuration file, running Bake will present you with the following options:--------------------------------------------------------------App : app
Path: /path-to/project/app--------------------------------------------------------------Interactive Bake Shell--------------------------------------------------------------[D]atabase Configuration[M]odel[V]iew[C]ontroller[P]roject[F]ixture[T]est case[Q]uitWhat would you like to Bake? (D/M/V/C/P/F/T/Q)>
Alternatively, you can run any of these commands directly from the command line:$ cake bake db_config$ cake bake model(continues on next page)

More topics

561

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$ cake bake view$ cake bake controller$ cake bake project$ cake bake fixture$ cake bake test
$ cake bake plugin plugin_name$ cake bake all
Changed in version 2.5: Test files produced by bake test include calls to PHPunit’s markTestIncomplete()107 to draw
attention to empty test methods. Before 2.5, empty tests pass silently.
Modify default HTML produced by “baked” templates
If you wish to modify the default HTML output produced by the “bake” command, follow these simple steps:
For baking custom views
1. Go into: lib/Cake/Console/Templates/default/views
2. Notice the 4 files there
3. Copy them to your: app/Console/Templates/[themename]/views
4. Make changes to the HTML output to control the way “bake” builds your views
The [themename] path segment should be the name of the bake theme that you are creating. Bake theme names need
to be unique, so don’t use ‘default’.
For baking custom projects
1. Go into: lib/Cake/Console/Templates/skel
2. Notice the base application files there
3. Copy them to your: app/Console/Templates/skel
4. Make changes to the HTML output to control the way “bake” builds your views
5. Pass the skeleton path parameter to the project task
cake bake project --skel Console/Templates/skel

Note:• You must run the specific project task cake bake project so that the path parameter can be passed.
• The template path is relative to the current path of the Command Line Interface.
• Since the full path to the skeleton needs to be manually entered, you can specify any directory holding yourtemplate build you want, including using multiple templates. (Unless CakePHP starts supporting overriding theskel folder like it does for views)
107 https://phpunit.de/manual/3.7/en/incomplete-and-skipped-tests.html

562

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

Schema management and migrations
The SchemaShell provides a functionality to create schema objects, schema sql dumps as well as create snapshots andrestore database snapshots.
Generating and using Schema files
A generated schema file allows you to easily transport a database agnostic schema. You can generate a schema file ofyour database using:$ Console/cake schema generateThis will generate a schema.php file in your app/Config/Schema directory.
Note: The schema shell will only process tables for which there are models defined. To force the schema shell toprocess all the tables, you must add the -f option in the command line.
To later rebuild the database schema from your previously made schema.php file run:
$ Console/cake schema create
This will drop and create the tables based on the contents of the schema.php.
Schema files can also be used to generate sql dump files. To generate a sql file containing the CREATE TABLE statements,run:$ Console/cake schema dump --write filename.sql
Where filename.sql is the desired filename for the sql dump. If you omit filename.sql the sql dump will be output tothe console but not written to a file.
CakeSchema callbacks
After generating a schema you might want to insert data on some tables to get your app started. This can beachieved through CakeSchema callbacks. Every schema file is generated with a before($event = array()) and aafter($event = array()) method.
The $event param holds an array with two keys. One to tell if a table is being dropped or created and another forerrors. Examples:array('drop' => 'posts', 'errors' => null)array('create' => 'posts', 'errors' => null)
Adding data to a posts table for example would like this:
App::uses('Post', 'Model');public function after($event = array()) {if (isset($event['create'])) {switch ($event['create']) {case 'posts':
App::uses('ClassRegistry', 'Utility');$post = ClassRegistry::init('Post');$post->create();(continues on next page)

More topics

563

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$post->save(array('Post' =>array('title' => 'CakePHP Schema Files')));break;}}}The before() and after() callbacks run each time a table is created or dropped on the current schema.
When inserting data to more than one table you’ll need to flush the database cache after each table is created. Cache
can be disable by setting $db->cacheSources = false in the before action().
public $connection = 'default';
public function before($event = array()) {$db = ConnectionManager::getDataSource($this->connection);$db->cacheSources = false;
return true;}
If you use models in your callbacks make sure to initialize them with the correct datasource, lest they fallback to theirdefault datasources:
public function before($event = array()) {$articles = ClassRegistry::init('Articles', array('ds' => $this->connection));// Do things with articles.
}

Writing CakePHP Schema by Hand
The CakeSchema class is the base class for all database schemas. Each schema class is able to generate a set of tables.
The schema shell console class SchemaShell in the lib/Cake/Console/Command directory interprets command
line, and base schema class can read from the database, or generate the database table.
CakeSchema can now locate, read and write schema files to plugins. The SchemaShell also exposes this functionality.
CakeSchema also supports tableParameters. Table Parameters are non column specific table information such as
collation, charset, comments, and table engine type. Each Dbo implements the tableParameters they support.

564

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

ExampleHere is a full example from the acl class/*** ACO - Access Control Object - Something that is wanted*/
public $acos = array('id' => array('type' => 'integer','null' => false,'default' => null,'length' => 10,'key' => 'primary'),'parent_id' => array('type' => 'integer','null' => true,'default' => null,'length' => 10),'model' => array('type' => 'string', 'null' => true),'foreign_key' => array('type' => 'integer','null' => true,'default' => null,'length' => 10),'alias' => array('type' => 'string', 'null' => true),'lft' => array('type' => 'integer','null' => true,'default' => null,'length' => 10),'rght' => array('type' => 'integer','null' => true,'default' => null,'length' => 10),'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)));

More topics

565

CakePHP Cookbook Documentation, Release 2.x

Columns
Each column is encoded as a key value associative array. The field name is the key of the field, the value is anotherarray with some of the following attributes.
Example column:'id' => array('type' => 'integer','null' => false,'default' => null,'length' => 10,'key' => 'primary'),key
The primary key defines the primary key index.
null
Is the field nullable?
default
What is the default value of the field?
limit
The limit of the type of the field.
lengthWhat is the length of the field?
type
One of the following types• integer• smallinteger• tinyinteger• biginteger• date• time• datetime• timestamp• boolean• float• string• text• binary
Changed in version 2.10.0: The smallinteger and tinyinteger types were added in 2.10.0

566

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

Table key indexes
The key name indexes is put in the table array instead of a field name.
columnThis is either a single column name or an array of columns.
e.g. Single'indexes' => array('PRIMARY' => array('column' => 'id','unique' => 1))
e.g. Multiple'indexes' => array('AB_KEY' => array('column' => array('a_id','b_id'),'unique' => 1))
uniqueIf the index is unique, set this to 1, otherwise 0.
Table key tableParameterstableParameters are supported only in MySQL.
You can use tableParameters to set a variety of MySQL specific settings.
• engine Control the storage engine used for your tables.
• charset Control the character set used for tables.
• encoding Control the encoding used for tables.
In addition to tableParameters MySQL dbo’s implement fieldParameters. fieldParameters allow you to control
MySQL specific settings per column.
• charset Set the character set used for a column• encoding Set the encoding used for a column
See below for examples on how to use table and field parameters in your schema files.
Using tableParameters in schema files
You use tableParameters just as you would any other key in a schema file. Much like indexes:
var $comments => array('id' => array('type' => 'integer',(continues on next page)

More topics

567

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'null' => false,'default' => 0,'key' => 'primary'),'post_id' => array('type' => 'integer', 'null' => false, 'default' => 0),'comment' => array('type' => 'text'),'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true),'post_id' => array('column' => 'post_id'),),'tableParameters' => array('engine' => 'InnoDB','charset' => 'latin1','collate' => 'latin1_general_ci'));
is an example of a table using tableParameters to set some database specific settings. If you use a schema file that
contains options and features your database does not implement, those options will be ignored.
Migrations with CakePHP schema shell
Migrations allow for versioning of your database schema, so that as you develop features you have an easy and databaseagnostic way to distribute database changes. Migrations are achieved through either SCM controlled schema files orschema snapshots. Versioning a schema file with the schema shell is quite easy. If you already have a schema filecreated running:$ Console/cake schema generate
Will bring up the following choices:
Generating Schema...
Schema file exists.
[O]verwrite[S]napshot[Q]uitWould you like to do? (o/s/q)
Choosing [s] (snapshot) will create an incremented schema.php. So if you have schema.php, it will create schema_2.php
and so on. You can then restore to any of these schema files at any time by running:$ cake schema update -s 2
Where 2 is the snapshot number you wish to run. The schema shell will prompt you to confirm you wish to performthe ALTER statements that represent the difference between the existing database the currently executing schema file.
You can perform a dry run by adding a --dry to your command.
Note: Please note that schema generation in 2.x does not handle foreign key constraints.

568

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

Workflow examples
Create schema and commit
On a project which use versioning, the usage of cake schema would follow these steps:
1. Create or modify your database tables
2. Execute cake schema to export a full description of your database
3. Commit the created or updated schema.php file:$ # once your database has been updated$ Console/cake schema generate$ git commit -a

Note: If the project is not versioned, managing schemas would be done through snapshots. (see previous section to
manage snapshots)

Getting the last changes
When you pull the last changes of your repository, and discover changes in the structure of the database (possibly
because of an error message saying you are missing a table):
1. Execute cake schema to update your database:$ git pull
$ Console/cake schema create$ Console/cake schema update
All these operations can be done in dry-run mode with a --dry option.
Rolling backIf at some point you need to revert and get back to the state in which you were before updating your database, you
should be informed that this is currently not supported by cake schema.
More specifically, you can’t automatically drop your tables once they have been created.
Using update will, on the contrary, drop any field which differ from the schema file:$ git revert HEAD
$ Console/cake schema update
Will bring up the following choices:
The following statements will run.
ALTER TABLE `roles`
DROP `position`;
Are you sure you want to alter the tables? (y/n)[n] >More topics

569

CakePHP Cookbook Documentation, Release 2.x

I18N shell
The i18n features of CakePHP use po files108 as their translation source. This makes them easily to integrate with tools
like poedit109 and other common translation tools.
The i18n shell provides a quick and easy way to generate po template files. These templates files can then be given
to translators so they can translate the strings in your application. Once you have translations done, pot files can bemerged with existing translations to help update your translations.
Generating POT files
POT files can be generated for an existing application using the extract command. This command will scan yourentire application for __() style function calls, and extract the message string. Each unique string in your application
will be combined into a single POT file:./Console/cake i18n extract
The above will run the extraction shell. In addition to extracting strings in __() methods, validation messages in models
will be extracted as well. The result of this command will be the file app/Locale/default.pot. You use the pot file
as a template for creating po files. If you are manually creating po files from the pot file, be sure to correctly set the
Plural-Forms header line.
Generating POT files for plugins
You can generate a POT file for a specific plugin using:./Console/cake i18n extract --plugin <Plugin>This will generate the required POT files used in the plugins.
Model validation messages
You can set the domain to be used for extracted validation messages in your models. If the model already has a$validationDomain property, the given validation domain will be ignored:./Console/cake i18n extract --validation-domain validation_errors
You can also prevent the shell from extracting validation messages:./Console/cake i18n extract --ignore-model-validation
108 https://en.wikipedia.org/wiki/GNU_gettext
109 https://www.poedit.net/

570

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

Excluding folders
You can pass a comma separated list of folders that you wish to be excluded. Any path containing a path segment withthe provided values will be ignored:./Console/cake i18n extract --exclude Test,Vendor

Skipping overwrite warnings for existing POT files
New in version 2.2.
By adding --overwrite, the shell script will no longer warn you if a POT file already exists and will overwrite by
default:./Console/cake i18n extract --overwrite

Extracting messages from the CakePHP core libraries
New in version 2.2.
By default, the extract shell script will ask you if you like to extract the messages used in the CakePHP core libraries.
Set --extract-core to yes or no to set the default behavior.
./Console/cake i18n extract --extract-core yesor./Console/cake i18n extract --extract-core no

Create the tables used by TranslateBehavior
The i18n shell can also be used to initialize the default tables used by the TranslateBehavior:./Console/cake i18n initdb
This will create the i18n table used by translate behavior.

ACL Shell
The AclShell is useful for managing and inspecting your Acl databases records. It’s often more convenient than adding
one time modifications to your controllers.
Most acl shell subcommands involve referencing aco/aro nodes. As there are two ‘forms’ of these nodes, there is two
notations in the shell:# A Model + foreign_key reference./Console/cake acl view aro Model.1# An alias path reference./Console/cake acl view aco root/controllers
Using a . indicates that you are going to use a bound record style reference while using a / indicates an alias path.
More topics

571

CakePHP Cookbook Documentation, Release 2.x

Installing the database tables
Before using the database ACL you’ll need to setup the tables. You can do that using:./Console/cake acl initdb

Create and delete nodes
You can use the create and delete subcommands to create and delete nodes:./Console/cake acl create aco controllers Posts./Console/cake acl create aco Posts index
Would create an aco record using an alias path. You could do the following as well:./Console/cake acl create aro Group.1
To create an aro node for the Group id = 1.
Grant and deny access
Use the grant command to grant ACL permissions. Once executed, the ARO specified (and its children, if any) will
have ALLOW access to the specified ACO action (and the ACO’s children, if any):./Console/cake acl grant Group.1 controllers/Posts
The above would grant all privileges. You could grant only the read privilege using the following:./Console/cake acl grant Group.1 controllers/Posts read
Denying permission works in the exact same way. The only difference is you switch ‘deny’ in for ‘grant’.
Check permissions
Use this command to check ACL permissions.
./Console/cake acl check Group.1 controllers/Posts readThe output will either be allowed or not allowed.
View the node trees
The view command will return the ARO or ACO tree. The optional node parameter allows you to return only a portionof the requested tree:./Console/cake acl view

572

Chapter 9. Shells, Tasks & Console Tools

CakePHP Cookbook Documentation, Release 2.x

Test shell
Once you’ve started writing Tests you can run them using the test shell.
For more information on basic usage of the test shell see Running tests from command line.
Changed in version 2.1: The test shell was added in 2.1. The 2.0 testsuite shell is still available but the new syntax
is preferred.

Upgrade shell
The upgrade shell will do most of the work to upgrade your CakePHP application from 1.3 to 2.0.
To run all upgrade steps:./Console/cake upgrade all
If you would like to see what the shell will do without modifying files perform a dry run first with –dry-run:./Console/cake upgrade all --dry-run
To upgrade your plugin run the command:./Console/cake upgrade all --plugin YourPluginName
You are able to run each upgrade step individually. To see all the steps available run the command:./Console/cake upgrade --help
Or visit the API docs110 for more info.
Upgrade Your App
Here is a guide to help you upgrade your CakePHP 1.3 app to 2.x using the upgrade shell. Your 1.3 app structure will
likely look like this:mywebsite/
app/
cake/
plugins/
vendors/.htaccessindex.php<- Your App<- 1.3 Version of CakePHP

The first step is to download or git clone the new version of CakePHP into another folder outside of your mywebsite
folder, we’ll call it cakephp. We don’t want the downloaded app folder to overwrite your app folder. Now is a goodtime to make a backup of your app folder, eg.: cp -R app app-backup.
Copy the cakephp/lib folder to your mywebsite/lib to setup the new CakePHP version in your app, eg.: cp -R../cakephp/lib .. Symlinking is a good alternative to copy as well, eg.: ln -s /var/www/cakephp/lib.
Before we can run the upgrade shell we need the new console scripts as well. Copy the cakephp/app/Console folder
into your mywebsite/app, eg.: cp -R ../cakephp/app/Console ./app.
Your folder structure should look like this now:
110 https://api.cakephp.org/2.x/class-UpgradeShell.html

More topics

573

CakePHP Cookbook Documentation, Release 2.x

mywebsite/
app/Console/app-backup/
cake/
lib/
Cake/
plugins/
vendors/.htaccessindex.php<- Your App<- Copied app/Console Folder<- Backup Copy of Your App<- 1.3 Version of CakePHP<- 2.x Version of CakePHP

Now we can run the upgrade shell by cd’ing into your app folder and running the command:./Console/cake upgrade all
This will do most of the work to upgrade your app to 2.x. Check things over in your upgraded app folder. If everything
looks good then congratulate yourself and delete your mywebsite/cake folder. Welcome to 2.x!

574

Chapter 9. Shells, Tasks & Console Tools

CHAPTER 10

Development

In this section we’ll cover the various aspects of developing a CakePHP application. Topics like Configuration, handlingerrors & exceptions, debugging, and testing will be covered.

Configuration
Configuring a CakePHP application is a piece of cake. After you have installed CakePHP, creating a basic web application requires only that you setup a database configuration.
There are, however, other optional configuration steps you can take in order to take advantage of CakePHP flexible
architecture. You can easily add to the functionality inherited from the CakePHP core, configure additional/different
URL mappings (routes), and define additional/different inflections.

Database Configuration
CakePHP expects database configuration details to be in a file at app/Config/database.php. An example databaseconfiguration file can be found at app/Config/database.php.default. A finished configuration should look something like this:class DATABASE_CONFIG {public $default = array('datasource' => 'Database/Mysql','persistent' => false,'host'=> 'localhost','login'=> 'cakephpuser','password'=> 'c4k3roxx!','database'=> 'my_cakephp_project','prefix'=> ''(continues on next page)575

CakePHP Cookbook Documentation, Release 2.x(continued from previous page));}
The $default connection array is used unless another connection is specified by the $useDbConfig property in amodel. For example, if my application has an additional legacy database in addition to the default one, I could use it in
my models by creating a new $legacy database connection array similar to the $default array, and by setting public$useDbConfig = 'legacy'; in the appropriate models.
Fill out the key/value pairs in the configuration array to best suit your needs.
datasource
The name of the datasource this configuration array is for. Examples: Database/Mysql, Database/Sqlserver,
Database/Postgres, Database/Sqlite. You can use plugin syntax to indicate plugin datasource to use.
persistent
Whether or not to use a persistent connection to the database. When using SQLServer you should not enable
persistent connections as it causes difficult to diagnose crashes in older versions of CakePHP or, as of version
2.10.2, an exception will be thrown.
host
The database server’s hostname (or IP address).
login
The username for the account.
password
The password for the account.
database
The name of the database for this connection to use.
prefix (optional)
The string that prefixes every table name in the database. If your tables don’t have prefixes, set this to an empty
string.
port (optional)
The TCP port or Unix socket used to connect to the server.
encoding
Indicates the character set to use when sending SQL statements to the server. This defaults to the database’s
default encoding for all databases other than DB2. If you wish to use UTF-8 encoding with mysql/mysqli connections you must use ‘utf8’ without the hyphen.
schemaUsed in PostgreSQL database setups to specify which schema to use.
unix_socket
Used by drivers that support it to connect via unix socket files. If you are using PostgreSQL and want to use unix
sockets, leave the host key blank.
ssl_key
The file path to the SSL key file. (Only supported by MySQL, requires PHP 5.3.7+).
ssl_cert
The file path to the SSL certificate file. (Only supported by MySQL, requires PHP 5.3.7+).
ssl_caThe file path to the SSL certificate authority. (Only supported by MySQL, requires PHP 5.3.7+).

576

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

settings
An array of key/value pairs that should be sent to the database server as SET commands when the connection iscreated. This option is only supported by the Mysql, Postgres, and Sqlserver datasources at this time.
Changed in version 2.4: The settings, ssl_key, ssl_cert and ssl_ca keys was added in 2.4.
Note: The prefix setting is for tables, not models. For example, if you create a join table for your Apple and Flavor
models, you name it prefix_apples_flavors (not prefix_apples_prefix_flavors), and set your prefix setting to ‘prefix_’.
At this point, you might want to take a look at the CakePHP Conventions. The correct naming for your tables (and the
addition of some columns) can score you some free functionality and help you avoid configuration. For example, if you
name your database table big_boxes, your model BigBox, your controller BigBoxesController, everything just works
together automatically. By convention, use underscores, lower case, and plural forms for your database table names for example: bakers, pastry_stores, and savory_cakes.

Additional Class Paths
It’s occasionally useful to be able to share MVC classes between applications on the same system. If you want the same
controller in both applications, you can use CakePHP’s bootstrap.php to bring these additional classes into view.
By using App::build() in bootstrap.php we can define additional paths where CakePHP will look for classes:
App::build(array('Model' => array('/path/to/models','/next/path/to/models'),'Model/Behavior' => array('/path/to/behaviors','/next/path/to/behaviors'),'Model/Datasource' => array('/path/to/datasources','/next/path/to/datasources'),'Model/Datasource/Database' => array('/path/to/databases','/next/path/to/database'),'Model/Datasource/Session' => array('/path/to/sessions','/next/path/to/sessions'),'Controller' => array('/path/to/controllers','/next/path/to/controllers'),'Controller/Component' => array('/path/to/components','/next/path/to/components'),'Controller/Component/Auth' => array((continues on next page)

Configuration577

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'/path/to/auths','/next/path/to/auths'),'Controller/Component/Acl' => array('/path/to/acls','/next/path/to/acls'),'View' => array('/path/to/views','/next/path/to/views'),'View/Helper' => array('/path/to/helpers','/next/path/to/helpers'),'Console' => array('/path/to/consoles','/next/path/to/consoles'),'Console/Command' => array('/path/to/commands','/next/path/to/commands'),'Console/Command/Task' => array('/path/to/tasks','/next/path/to/tasks'),'Lib' => array('/path/to/libs','/next/path/to/libs'),'Locale' => array('/path/to/locales','/next/path/to/locales'),'Vendor' => array('/path/to/vendors','/next/path/to/vendors'),'Plugin' => array('/path/to/plugins','/next/path/to/plugins'),));

Note: All additional path configuration should be done at the top of your application’s bootstrap.php. This will ensurethat the paths are available for the rest of your application.

578

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Core Configuration
Each application in CakePHP contains a configuration file, app/Config/core.php, to determine CakePHP’s internal
behavior. This file is a collection of Configure class variable definitions and constant definitions that determine how
your application behaves. Before we dive into those particular variables, you’ll need to be familiar with Configure,
CakePHP’s configuration registry class.
CakePHP Core Configuration
The Configure class is used to manage a set of core CakePHP configuration variables. These variables can be foundin app/Config/core.php. Below is a description of each variable and how it affects your CakePHP application.
debug
Changes CakePHP debugging output.
• 0 = Production mode. No output.
• 1 = Show errors and warnings.
• 2 = Show errors, warnings, and SQL. [SQL log is only shown when you add $this->element(‘sql_dump’)
to your view or layout.]
Error
Configure the Error handler used to handle errors for your application.
By default
ErrorHandler::handleError() is used. It will display errors using Debugger, when debug > 0 andlog errors with CakeLog when debug = 0.
Sub-keys:• handler - callback - The callback to handle errors. You can set this to any callback type, including anonymous functions.
• level - int - The level of errors you are interested in capturing.
• trace - boolean - Include stack traces for errors in log files.
Exception
Configure the Exception handler used for uncaught exceptions. By default, ErrorHandler::handleException()is used. It will display a HTML page for the exception, and while debug > 0, framework errors like Missing
Controller will be displayed. When debug = 0, framework errors will be coerced into generic HTTP errors. For
more information on Exception handling, see the Exceptions section.
App.baseUrl
If you don’t want or can’t get mod_rewrite (or some other compatible module) up and running on your server,
you’ll need to use CakePHP’s built-in pretty URLs. In /app/Config/core.php, uncomment the line that looks
like:Configure::write('App.baseUrl', env('SCRIPT_NAME'));
Also remove these .htaccess files:/.htaccess/app/.htaccess/app/webroot/.htaccessThis will make your URLs look like www.example.com/index.php/controllername/actionname/param rather thanwww.example.com/controllername/actionname/param.
If you are installing CakePHP on a webserver besides Apache, you can find instructions for getting URL rewriting
working for other servers under the URL Rewriting section.
Configuration579

CakePHP Cookbook Documentation, Release 2.x

App.fullBaseUrl
The fully qualified domain name (including protocol) to your application’s root. To configure CakePHP to use aparticular domain URL for any URL generation inside the application, set this configuration variable. This will
override the automatic detection of full base URL and can be useful when generating links from the CLI (e.g.
sending emails). If the application runs in a subfolder, you should also set App.base.
App.base
The base directory the app resides in. Should be used if the application runs in a subfolder and App.
fullBaseUrl is set.
App.encoding
Define what encoding your application uses. This encoding is used to generate the charset in the layout, andencode entities. It should match the encoding values specified for your database.
Routing.prefixes
Un-comment this definition if you’d like to take advantage of CakePHP prefixed routes like admin. Set thisvariable with an array of prefix names of the routes you’d like to use. More on this later.
Cache.disable
When set to true, persistent caching is disabled site-wide. This will make all read/writes to Cache fail.
Cache.check
If set to true, enables view caching. Enabling is still needed in the controllers, but this variable enables thedetection of those settings.
SessionContains an array of settings to use for session configuration. The defaults key is used to define a default preset
to use for sessions, any settings declared here will override the settings of the default config.
Sub-keys• name - The name of the cookie to use. Defaults to ‘CAKEPHP’• timeout - The number of minutes you want sessions to live for. This timeout is handled by CakePHP• cookieTimeout - The number of minutes you want session cookies to live for.
• checkAgent - Do you want the user agent to be checked when starting sessions? You might want to set
the value to false, when dealing with older versions of IE, Chrome Frame or certain web-browsing devices
and AJAX• defaults - The default configuration set to use as a basis for your session. There are four builtins: php,
cake, cache, database.
• handler - Can be used to enable a custom session handler. Expects an array of callables, that can be usedwith session_save_handler. Using this option will automatically add session.save_handler to the ini array.
• autoRegenerate - Enabling this setting, turns on automatic renewal of sessions, and sessionids that change
frequently. See CakeSession::$requestCountdown.
• ini - An associative array of additional ini values to set.
The built-in defaults are:• ‘php’ - Uses settings defined in your php.ini.
• ‘cake’ - Saves session files in CakePHP’s /tmp directory.
• ‘database’ - Uses CakePHP’s database sessions.
• ‘cache’ - Use the Cache class to save sessions.
To define a custom session handler, save it at app/Model/Datasource/Session/<name>.php. Make sure theclass implements CakeSessionHandlerInterface and set Session.handler to <name>580

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

To use database sessions, run the app/Config/Schema/sessions.php schema using the cake shell command:cake schema create Sessions
Security.salt
A random string used in security hashing.
Security.cipherSeed
A random numeric string (digits only) used to encrypt/decrypt strings.
Asset.timestamp
Appends a timestamp which is last modified time of the particular file at the end of asset files URLs (CSS,JavaScript, Image) when using proper helpers. Valid values: (boolean) false - Doesn’t do anything (default)(boolean) true - Appends the timestamp when debug > 0 (string) ‘force’ - Appends the timestamp when debug>= 0
Acl.classname, Acl.database
Constants used for CakePHP’s Access Control List functionality. See the Access Control Lists chapter for more
information.
Note: Cache configuration is also found in core.php — We’ll be covering that later on, so stay tuned.
The Configure class can be used to read and write core configuration settings on the fly. This can be especially handy
if you want to turn the debug setting on for a limited section of logic in your application, for instance.
Configuration Constants
While most configuration options are handled by Configure, there are a few constants that CakePHP uses during runtime.
constant LOG_ERROR
Error constant. Used for differentiating error logging and debugging. Currently PHP supports LOG_DEBUG.
Core Cache Configuration
CakePHP uses two cache configurations internally. _cake_model_ and _cake_core_. _cake_core_ is used to store
file paths, and object locations. _cake_model_ is used to store schema descriptions, and source listings for datasources.
Using a fast cache storage like APC or Memcached is recommended for these configurations, as they are read on every
request. By default both of these configurations expire every 10 seconds when debug is greater than 0.
As with all cached data stored in Cache you can clear data using Cache::clear().

Configure Class
class Configure
Despite few things needing to be configured in CakePHP, it’s sometimes useful to have your own configuration rules
for your application. In the past you may have defined custom configuration values by defining variable or constants insome files. Doing so forces you to include that configuration file every time you needed to use those values.
CakePHP’s Configure class can be used to store and retrieve application or runtime specific values. Be careful, thisclass allows you to store anything in it, then use it in any other part of your code: a sure temptation to break the MVC
pattern CakePHP was designed for. The main goal of Configure class is to keep centralized variables that can be shared
between many objects. Remember to try to live by “convention over configuration” and you won’t end up breaking the
MVC structure we’ve set in place.
Configuration

581

CakePHP Cookbook Documentation, Release 2.x

This class can be called from anywhere within your application, in a static context:
Configure::read('debug');
static Configure::write($key, $value)
Parameters• $key (string) – The key to write, can be a dot notation value.
• $value (mixed) – The value to store.
Use write() to store data in the application’s configuration:
Configure::write('Company.name','Pizza, Inc.');Configure::write('Company.slogan','Pizza for your body and soul');Note: The dot notation used in the $key parameter can be used to organize your configuration settings into
logical groups.
The above example could also be written in a single call:Configure::write('Company',array('name' => 'Pizza, Inc.','slogan' => 'Pizza for your body and soul'));
You can use Configure::write('debug', $int) to switch between debug and production modes on the
fly. This is especially handy for AMF or SOAP interactions where debugging information can cause parsing
problems.
static Configure::read($key = null)Parameters• $key (string) – The key to read, can be a dot notation value
Used to read configuration data from the application. Defaults to CakePHP’s important debug value. If a key issupplied, the data is returned. Using our examples from write() above, we can read that data back:Configure::read('Company.name');Configure::read('Company.slogan');//yields: 'Pizza, Inc.'//yields: 'Pizza for your body//and soul'Configure::read('Company');//yields:
array('name' => 'Pizza, Inc.', 'slogan' => 'Pizza for your body and soul');If $key is left null, all values in Configure will be returned. If the value corresponding to the specified $key does
not exist then null will be returned.
static Configure::consume($key)
Parameters
582

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x• $key (string) – The key to read, can use be a dot notation value
Read and delete a key from Configure. This is useful when you want to combine reading and deleting values ina single operation.
static Configure::check($key)
Parameters• $key (string) – The key to check.
Used to check if a key/path exists and has not-null value.
New in version 2.3: Configure::check() was added in 2.3
static Configure::delete($key)
Parameters• $key (string) – The key to delete, can be a dot notation value
Used to delete information from the application’s configuration:
Configure::delete('Company.name');static Configure::version
Returns the CakePHP version for the current application.
static Configure::config($name, $reader)Parameters• $name (string) – The name of the reader being attached.
• $reader (ConfigReaderInterface) – The reader instance being attached.
Attach a configuration reader to Configure. Attached readers can then be used to load configuration files. See
Loading configuration files for more information on how to read configuration files.
static Configure::configured($name = null)Parameters• $name (string) – The name of the reader to check, if null a list of all attached readers will
be returned.
Either check that a reader with a given name is attached, or get the list of attached readers.
static Configure::drop($name)Drops a connected reader object.

Reading and writing configuration files
CakePHP comes with two built-in configuration file readers. PhpReader is able to read PHP config files, in the sameformat that Configure has historically read. IniReader is able to read ini config files. See the PHP documentation111
for more information on the specifics of ini files. To use a core config reader, you’ll need to attach it to Configure using
Configure::config():
111 https://www.php.net/parse_ini_file

Configuration

583

CakePHP Cookbook Documentation, Release 2.x

App::uses('PhpReader', 'Configure');// Read config files from app/Config
Configure::config('default', new PhpReader());// Read config files from another path.
Configure::config('default', new PhpReader('/path/to/your/config/files/'));
You can have multiple readers attached to Configure, each reading different kinds of configuration files, or readingfrom different types of sources. You can interact with attached readers using a few other methods on Configure. To see
which reader aliases are attached you can use Configure::configured():// Get the array of aliases for attached readers.
Configure::configured();// Check if a specific reader is attached
Configure::configured('default');
You can also remove attached readers. Configure::drop('default') would remove the default reader alias. Any
future attempts to load configuration files with that reader would fail.
Loading configuration files
static Configure::load($key, $config = 'default', $merge = true)
Parameters• $key (string) – The identifier of the configuration file to load.
• $config (string) – The alias of the configured reader.
• $merge (boolean) – Whether or not the contents of the read file should be merged, or overwrite the existing values.
Once you’ve attached a config reader to Configure you can load configuration files:// Load my_file.php using the 'default' reader object.
Configure::load('my_file', 'default');
Loaded configuration files merge their data with the existing runtime configuration in Configure. This allows you to
overwrite and add new values into the existing runtime configuration. By setting $merge to false, values will not everoverwrite the existing configuration.
Creating or modifying configuration files
static Configure::dump($key, $config = 'default', $keys = array())
Parameters• $key (string) – The name of the file/stored configuration to be created.
• $config (string) – The name of the reader to store the data with.
• $keys (array) – The list of top-level keys to save. Defaults to all keys.
Dumps all or some of the data in Configure into a file or storage system supported by a config reader. The serialization
format is decided by the config reader attached as $config. For example, if the ‘default’ adapter is a PhpReader, thegenerated file will be a PHP configuration file loadable by the PhpReader
584

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Given that the ‘default’ reader is an instance of PhpReader. Save all data in Configure to the file my_config.php:
Configure::dump('my_config.php', 'default');
Save only the error handling configuration:
Configure::dump('error.php', 'default', array('Error', 'Exception'));Configure::dump() can be used to either modify or overwrite configuration files that are readable with
Configure::load()
New in version 2.2: Configure::dump() was added in 2.2.
Storing runtime configuration
static Configure::store($name, $cacheConfig = 'default', $data = null)Parameters• $name (string) – The storage key for the cache file.
• $cacheConfig (string) – The name of the cache configuration to store the configuration
data with.
• $data (mixed) – Either the data to store, or leave null to store all data in Configure.
You can also store runtime configuration values for use in a future request. Since configure only remembers values forthe current request, you will need to store any modified configuration information if you want to use it in subsequent
requests:// Store the current configuration in the 'user_1234' key in the 'default' cache.
Configure::store('user_1234', 'default');
Stored configuration data is persisted in the Cache class. This allows you to store Configuration information in any
storage engine that Cache can talk to.
Restoring runtime configuration
static Configure::restore($name, $cacheConfig = 'default')
Parameters• $name (string) – The storage key to load.
• $cacheConfig (string) – The cache configuration to load the data from.
Once you’ve stored runtime configuration, you’ll probably need to restore it so you can access it again.
Configure::restore() does exactly that:// restore runtime configuration from the cache.
Configure::restore('user_1234', 'default');
When restoring configuration information it’s important to restore it with the same key, and cache configuration as wasused to store it. Restored information is merged on top of the existing runtime configuration.

Configuration

585

CakePHP Cookbook Documentation, Release 2.x

Creating your own Configuration readersSince configuration readers are an extensible part of CakePHP, you can create configuration readers in your applicationand plugins. Configuration readers need to implement the ConfigReaderInterface. This interface defines a readmethod, as the only required method. If you really like XML files, you could create a simple Xml config reader for you
application:// in app/Lib/Configure/MyXmlReader.php
App::uses('Xml', 'Utility');class MyXmlReader implements ConfigReaderInterface {public function __construct($path = null) {if (!$path) {$path = APP . 'Config' . DS;}$this->_path = $path;}public function read($key) {$xml = Xml::build($this->_path . $key . '.xml');
return Xml::toArray($xml);}// As of 2.3 a dump() method is also required
public function dump($key, $data) {// code to dump data to file}}
In your app/Config/bootstrap.php you could attach this reader and use it:
App::uses('MyXmlReader', 'Configure');Configure::config('xml', new MyXmlReader());...
Configure::load('my_xml');

Warning: It is not a good idea to call your custom configure class XmlReader because that class name is aninternal PHP one already: XMLReader112
The read() method of a config reader, must return an array of the configuration information that the resource named$key contains.
interface ConfigReaderInterface
Defines the interface used by classes that read configuration data and store it in Configure
ConfigReaderInterface::read($key)
Parameters• $key (string) – The key name or identifier to load.
This method should load/parse the configuration data identified by $key and return an array of data in the file.
112 https://www.php.net/manual/en/book.xmlreader.php

586

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

ConfigReaderInterface::dump($key, $data)
Parameters• $key (string) – The identifier to write to.
• $data (array) – The data to dump.
This method should dump/store the provided configuration data to a key identified by $key.
New in version 2.3: ConfigReaderInterface::dump() was added in 2.3.
exception ConfigureException
Thrown when errors occur when loading/storing/restoring configuration data. ConfigReaderInterface implementations should throw this exception when they encounter an error.
Built-in Configuration readersclass PhpReader
Allows you to read configuration files that are stored as plain PHP files. You can read either files from yourapp/Config or from plugin configs directories by using plugin syntax. Files must contain a $config variable.
An example configuration file would look like:$config = array('debug' => 0,'Security' => array('salt' => 'its-secret'),'Exception' => array('handler' => 'ErrorHandler::handleException','renderer' => 'ExceptionRenderer','log' => true));
Files without $config will cause an ConfigureException
Load your custom configuration file by inserting the following in app/Config/bootstrap.php:
Configure::load('customConfig');
class IniReader
Allows you to read configuration files that are stored as plain .ini files. The ini files must be compatible with
PHP’s parse_ini_file function, and benefit from the following improvements• dot separated values are expanded into arrays.
• boolean-ish values like ‘on’ and ‘off’ are converted to booleans.
An example ini file would look like:debug = 0
Security.salt = its-secret[Exception]
handler = ErrorHandler::handleException(continues on next page)

Configuration

587

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

renderer = ExceptionRendererlog = true
The above ini file, would result in the same end configuration data as the PHP example above. Array structures
can be created either through dot separated values, or sections. Sections can contain dot separated keys for deeper
nesting.

Inflection Configuration
CakePHP’s naming conventions can be really nice - you can name your database table big_boxes, your model BigBox,
your controller BigBoxesController, and everything just works together automatically. The way CakePHP knows how
to tie things together is by inflecting the words between their singular and plural forms.
There are occasions (especially for our non-English speaking friends) where you may run into situations where
CakePHP’s Inflector (the class that pluralizes, singularizes, camelCases, and under_scores) might not work as you’d
like. If CakePHP won’t recognize your Foci or Fish, you can tell CakePHP about your special cases.
Loading custom inflections
You can use Inflector::rules() in the file app/Config/bootstrap.php to load custom inflections:
Inflector::rules('singular', array('rules' => array('/^(bil)er$/i' => '\1','/^(inflec|contribu)tors$/i' => '\1ta'),'uninflected' => array('singulars'),'irregular' => array('spins' => 'spinor')));or:Inflector::rules('plural', array('irregular' => array('phylum' => 'phyla')));
Will merge the supplied rules into the inflection sets defined in lib/Cake/Utility/Inflector.php, with the added rules
taking precedence over the core rules.

Bootstrapping CakePHP
If you have any additional configuration needs, use CakePHP’s bootstrap file, found in app/Config/bootstrap.php. This
file is executed just after CakePHP’s core bootstrapping.
This file is ideal for a number of common bootstrapping tasks:• Defining convenience functions.
• Registering global constants.
• Defining additional model, view, and controller paths.
• Creating cache configurations.
• Configuring inflections.
• Loading configuration files.
588

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Be careful to maintain the MVC software design pattern when you add things to the bootstrap file: it might be tempting
to place formatting functions there in order to use them in your controllers.
Resist the urge. You’ll be glad you did later on down the line.
You might also consider placing things in the AppController class. This class is a parent class to all of the controllers
in your application. AppController is a handy place to use controller callbacks and define methods to be used by allof your controllers.

RoutingRouting is a feature that maps URLs to controller actions. It was added to CakePHP to make pretty URLs more
configurable and flexible. Using Apache’s mod_rewrite is not required for using routes, but it will make your address
bar look much more tidy.
Routing in CakePHP also encompasses the idea of reverse routing, where an array of parameters can be reversed intoa string URL. By using reverse routing, you can easily re-factor your application’s URL structure without having to
update all your code.

Routes Configuration
Routes in an application are configured in app/Config/routes.php. This file is included by the Dispatcher whenhandling routes and allows you to define application specific routes you want used. Routes declared in this file areprocessed top to bottom when incoming requests are matched. This means that the order you place routes can affect
how routes are parsed. It’s generally a good idea to place most frequently visited routes at the top of the routes file if
possible. This will save having to check a number of routes that won’t match on each request.
Routes are parsed and matched, in the order they are connected in. If you define two similar routes, the first definedroute will have higher priority over the one defined latter. After connecting routes you can manipulate the order ofroutes using Router::promote().
CakePHP also comes with a few default routes to get you started. These can be disabled later on once you are sure you
don’t need them. See Disabling the Default Routes on how to disable the default routing.

Default Routing
Before you learn about configuring your own routes, you should know that CakePHP comes configured with a default
set of routes. CakePHP’s default routing will get you pretty far in any application. You can access an action directly
via the URL by putting its name in the request. You can also pass parameters to your controller actions using the URL.
// URL pattern default routes:
http://example.com/controller/action/param1/param2/param3
The URL /posts/view maps to the view() action of the PostsController, and /products/view_clearance maps to theview_clearance() action of the ProductsController. If no action is specified in the URL, the index() method is assumed.
The default routing setup also allows you to pass parameters to your actions using the URL. A request for /posts/view/25
would be equivalent to calling view(25) on the PostsController, for example. The default routing also provides routes
for plugins, and prefix routes should you choose to use those features.
The built-in routes live in Cake/Config/routes.php. You can disable the default routing by removing them fromyour application’s routes.php file.

Routing589

CakePHP Cookbook Documentation, Release 2.x

Connecting Routes
Defining your own routes allows you to define how your application will respond to a given URL. Define your own
routes in the app/Config/routes.php file using the Router::connect() method.
The connect() method takes up to three parameters: the URL you wish to match, the default values for your routeelements, and regular expression rules to help the router match elements in the URL.
The basic format for a route definition is:Router::connect('URL',
array('default' => 'defaultValue'),array('option' => 'matchingRegex'));
The first parameter is used to tell the router what sort of URL you’re trying to control. The URL is a normal slash
delimited string, but can also contain a wildcard (*) or Route Elements. Using a wildcard tells the router that you arewilling to accept any additional arguments supplied. Routes without a * only match the exact template pattern supplied.
Once you’ve specified a URL, you use the last two parameters of connect() to tell CakePHP what to do with a request
once it has been matched. The second parameter is an associative array. The keys of the array should be named after
the route elements in the URL, or the default elements: :controller, :action, and :plugin. The values in thearray are the default values for those keys. Let’s look at some basic examples before we start using the third parameterof connect():
Router::connect('/pages/*',array('controller' => 'pages', 'action' => 'display'));
This route is found in the routes.php file distributed with CakePHP. This route matches any URL starting with /pages/
and hands it to the display() action of the PagesController(); The request /pages/products would be mapped toPagesController->display('products').
In addition to the greedy star /* there is also the /** trailing star syntax. Using a trailing double star, will capture theremainder of a URL as a single passed argument. This is useful when you want to use an argument that included a /in it:
Router::connect('/pages/**',
array('controller' => 'pages', 'action' => 'show'));
The incoming URL of /pages/the-example-/-and-proof would result in a single passed argument ofthe-example-/-and-proof.
New in version 2.1: The trailing double star was added in 2.1.
You can use the second parameter of Router::connect() to provide any routing parameters that are composed ofthe default values of the route:
Router::connect('/government',array('controller' => 'pages', 'action' => 'display', 5));590

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

This example shows how you can use the second parameter of connect() to define default parameters. If you built asite that features products for different categories of customers, you might consider creating a route. This allows you
link to /government rather than /pages/display/5.
Note: Although you can connect alternate routes, the default routes will continue to work. In this setting, you canaccess a single piece of content from 2 different URLs. See Disabling the Default Routes to disable default routes, andonly provide the URLs you define.
Another common use for the Router is to define an “alias” for a controller. Let’s say that instead of accessing our regular
URL at /users/some_action/5, we’d like to be able to access it by /cooks/some_action/5. The following routeeasily takes care of that:
Router::connect('/cooks/:action/*', array('controller' => 'users'));
This is telling the Router that any url beginning with /cooks/ should be sent to the users controller. The action called
will depend on the value of the :action parameter. By using Route Elements, you can create variable routes, that
accept user input or variables. The above route also uses the greedy star. The greedy star indicates to Router that thisroute should accept any additional positional arguments given. These arguments will be made available in the Passed
Arguments array.
When generating URLs, routes are used too.
Using array('controller' => 'users', 'action' =>'some_action', 5) as a url will output /cooks/some_action/5 if the above route is the first match found.
By default all named and passed arguments are extracted from URLs matching greedy templates. However, you canconfigure how and which named arguments are parsed using Router::connectNamed() if you need to.
Route Elements
You can specify your own route elements and doing so gives you the power to define places in the URL where parameters for controller actions should lie. When a request is made, the values for these route elements are found in$this->request->params on the controller. This is different than how named parameters are handled, so note thedifference: named parameters (/controller/action/name:value) are found in $this->request->params['named'],whereas custom route element data is found in $this->request->params. When you define a custom route element,
you can optionally specify a regular expression - this tells CakePHP how to know if the URL is correctly formed ornot. If you choose to not provide a regular expression, any non / will be treated as part of the parameter:
Router::connect('/:controller/:id',array('action' => 'view'),array('id' => '[0-9]+'));This simple example illustrates how to create a quick way to view models from any controller by crafting a URL thatlooks like /controllername/:id. The URL provided to connect() specifies two route elements: :controller and:id. The :controller element is a CakePHP default route element, so the router knows how to match and identify
controller names in URLs. The :id element is a custom route element, and must be further clarified by specifying amatching regular expression in the third parameter of connect().
Note: Patterns used for route elements must not contain any capturing groups. If they do, Router will not function
correctly.

Routing591

CakePHP Cookbook Documentation, Release 2.x

Once this route has been defined, requesting /apples/5 is the same as requesting /apples/view/5. Both would
call the view() method of the ApplesController. Inside the view() method, you would need to access the passed ID at$this->request->params['id'].
If you have a single controller in your application and you do not want the controller name to appear in the URL, you
can map all URLs to actions in your controller. For example, to map all URLs to actions of the home controller, e.g
have URLs like /demo instead of /home/demo, you can do the following:
Router::connect('/:action', array('controller' => 'home'));If you would like to provide a case insensitive URL, you can use regular expression inline modifiers:
Router::connect('/:userShortcut',array('controller' => 'teachers', 'action' => 'profile', 1),
array('userShortcut' => '(?i:principal)'));
One more example, and you’ll be a routing pro:
Router::connect('/:controller/:year/:month/:day',array('action' => 'index'),array('year' => '[12][0-9]{3}','month' => '0[1-9]|1[012]','day' => '0[1-9]|[12][0-9]|3[01]'));This is rather involved, but shows how powerful routes can really become. The URL supplied has four route elements.
The first is familiar to us: it’s a default route element that tells CakePHP to expect a controller name.
Next, we specify some default values. Regardless of the controller, we want the index() action to be called.
Finally, we specify some regular expressions that will match years, months and days in numerical form. Note thatparenthesis (grouping) are not supported in the regular expressions. You can still specify alternates, as above, but notgrouped with parenthesis.
Once defined, this route will match /articles/2007/02/01, /posts/2004/11/16, handing the requests to theindex() actions of their respective controllers, with the date parameters in $this->request->params.
There are several route elements that have special meaning in CakePHP, and should not be used unless you want the
special meaning• controller Used to name the controller for a route.
• action Used to name the controller action for a route.
• plugin Used to name the plugin a controller is located in.
• prefix Used for Prefix Routing• ext Used for File Extensions routing.

592

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Passing Parameters to Action
When connecting routes using Route Elements you may want to have routed elements be passed arguments instead. By
using the 3rd argument of Router::connect() you can define which route elements should also be made available
as passed arguments:// SomeController.php
public function view($articleId = null, $slug = null) {// some code here...
}// routes.php
Router::connect('/blog/:id-:slug', // E.g. /blog/3-CakePHP_Rocksarray('controller' => 'blog', 'action' => 'view'),array(// order matters since this will simply map ":id" to// $articleId in your action'pass' => array('id', 'slug'),'id' => '[0-9]+'));
And now, thanks to the reverse routing capabilities, you can pass in the url array like below and CakePHP will know
how to form the URL as defined in the routes:// view.ctp// this will return a link to /blog/3-CakePHP_Rocksecho $this->Html->link('CakePHP Rocks', array('controller' => 'blog','action' => 'view','id' => 3,'slug' => 'CakePHP_Rocks'));

Per-Route Named Parameters
While you can control named parameters on a global scale using Router::connectNamed() you can also control
named parameter behavior at the route level using the 3rd argument of Router::connect():
Router::connect('/:controller/:action/*',array(),
array('named' => array('wibble','fish' => array('action' => 'index'),'fizz' => array('controller' => array('comments', 'other')),'buzz' => 'val-[\d]+')));Routing593

CakePHP Cookbook Documentation, Release 2.x

The above route definition uses the named key to define how several named parameters should be treated. Lets go
through each of the various rules one-by-one:• ‘wibble’ has no additional information. This means it will always parse if found in a URL matching this route.
• ‘fish’ has an array of conditions, containing the ‘action’ key. This means that fish will only be parsed as a named
parameter if the action is also index.
• ‘fizz’ also has an array of conditions. However, it contains two controllers, this means that ‘fizz’ will only beparsed if the controller matches one of the names in the array.
• ‘buzz’ has a string condition. String conditions are treated as regular expression fragments. Only values for buzz
matching the pattern will be parsed.
If a named parameter is used and it does not match the provided criteria, it will be treated as a passed argument insteadof a named parameter.
Prefix Routing
Many applications require an administration section where privileged users can make changes. This is often done
through a special URL such as /admin/users/edit/5. In CakePHP, prefix routing can be enabled from within thecore configuration file by setting the prefixes with Routing.prefixes. Note that prefixes, although related to the router,are to be configured in app/Config/core.php:
Configure::write('Routing.prefixes', array('admin'));In your controller, any action with an admin_ prefix will be called. Using our users example, accessing the URL /admin/users/edit/5 would call the method admin_edit of our UsersController passing 5 as the first parameter.
The view file used would be app/View/Users/admin_edit.ctp
You can map the URL /admin to your admin_index action of pages controller using following route:
Router::connect('/admin',array('controller' => 'pages', 'action' => 'index', 'admin' => true));
You can configure the Router to use multiple prefixes too. By adding additional values to Routing.prefixes. If youset:
Configure::write('Routing.prefixes', array('admin', 'manager'));
CakePHP will automatically generate routes for both the admin and manager prefixes. Each configured prefix will havethe following routes generated for it:
Router::connect("/{$prefix}/:plugin/:controller",
array('action' => 'index', 'prefix' => $prefix, $prefix => true));
Router::connect("/{$prefix}/:plugin/:controller/:action/*",array('prefix' => $prefix, $prefix => true));
Router::connect("/{$prefix}/:controller",
array('action' => 'index', 'prefix' => $prefix, $prefix => true)(continues on next page)

594

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x(continued from previous page));
Router::connect("/{$prefix}/:controller/:action/*",array('prefix' => $prefix, $prefix => true));
Much like admin routing all prefix actions should be prefixed with the prefix name. So /manager/posts/add would
map to PostsController::manager_add().
Additionally, the current prefix will be available from the controller methods through $this->request->prefix
When using prefix routes it’s important to remember, using the HTML helper to build your links will help maintain theprefix calls. Here’s how to build this link using the HTML helper:// Go into a prefixed route.
echo $this->Html->link('Manage posts',array('manager' => true, 'controller' => 'posts', 'action' => 'add'));// leave a prefix
echo $this->Html->link('View Post',
array('manager' => false, 'controller' => 'posts', 'action' => 'view', 5));Plugin Routing
Plugin routing uses the plugin key. You can create links that point to a plugin, but adding the plugin key to your URL
array:
echo $this->Html->link('New todo',
array('plugin' => 'todo', 'controller' => 'todo_items', 'action' => 'create'));
Conversely if the active request is a plugin request and you want to create a link that has no plugin you can do thefollowing:echo $this->Html->link('New todo',
array('plugin' => null, 'controller' => 'users', 'action' => 'profile'));
By setting plugin => null you tell the Router that you want to create a link that is not part of a plugin.

Routing595

CakePHP Cookbook Documentation, Release 2.x

File Extensions
To handle different file extensions with your routes, you need one extra line in your routes config file:
Router::parseExtensions('html', 'rss');
This will tell the router to remove any matching file extensions, and then parse what remains.
If you want to create a URL such as /page/title-of-page.html you would create your route as illustrated below:
Router::connect('/page/:title',
array('controller' => 'pages', 'action' => 'view'),array('pass' => array('title')));
Then to create links which map back to the routes simply use:
$this->Html->link('Link title',
array('controller' => 'pages','action' => 'view','title' => 'super-article','ext' => 'html'));
File extensions are used by RequestHandlerComponent to do automatic view switching based on content types. See
the RequestHandlerComponent for more information.
Using Additional Conditions When Matching Routes
When creating routes you might want to restrict certain URL’s based on specific request/environment settings. A
good example of this is REST routing. You can specify additional conditions in the $defaults argument for
Router::connect(). By default CakePHP exposes 3 environment conditions, but you can add more using Custom
Route Classes. The built-in options are:• [type] Only match requests for specific content types.
• [method] Only match requests with specific HTTP verbs.
• [server] Only match when $_SERVER[‘SERVER_NAME’] matches the given value.
We’ll provide a simple example here of how you can use the [method] option to create a custom RESTful route:
Router::connect("/:controller/:id",array("action" => "edit", "[method]" => "PUT"),
array("id" => "[0-9]+"));The above route will only match for PUT requests. Using these conditions, you can create custom REST routing, orother request data dependent information.

596

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Passed Arguments
Passed arguments are additional arguments or path segments that are used when making a request. They are often used
to pass parameters to your controller methods.
http://localhost/calendars/view/recent/markIn the above example, both recent and mark are passed arguments to CalendarsController::view(). Passed
arguments are given to your controllers in three ways. First as arguments to the action method called, and secondly they are available in $this->request->params['pass'] as a numerically indexed array. Lastly there is$this->passedArgs available in the same way as the second one. When using custom routes you can force particular parameters to go into the passed arguments as well.
If you were to visit the previously mentioned URL, and you had a controller action that looked like:
CalendarsController extends AppController {public function view($arg1, $arg2) {debug(func_get_args());}}
You would get the following output:
Array([0] => recent[1] => mark)This same data is also available at $this->request->params['pass'] and $this->passedArgs in your controllers, views, and helpers. The values in the pass array are numerically indexed based on the order they appear in thecalled URL:
debug($this->request->params['pass']);debug($this->passedArgs);
Either of the above would output:
Array([0] => recent[1] => mark)Note: $this->passedArgs may also contain named parameters as a named array mixed with Passed arguments.
When generating URLs, using a routing array you add passed arguments as values without string keys in the array:
array('controller' => 'posts', 'action' => 'view', 5)Since 5 has a numeric key, it is treated as a passed argument.

Routing597

CakePHP Cookbook Documentation, Release 2.x

Named Parameters
You can name parameters and send their values using the URL. A request for /posts/view/title:first/
category:general would result in a call to the view() action of the PostsController. In that action, you’d findthe values of the title and category parameters inside $this->params['named']. They are also available inside$this->passedArgs. In both cases you can access named parameters using their name as an index. If named parameters are omitted, they will not be set.
Note: What is parsed as a named parameter is controlled by Router::connectNamed(). If your named parameters
are not reverse routing, or parsing correctly, you will need to inform Router about them.
Some summarizing examples for default routes might prove helpful:
URL to controller action mapping using default routes:
URL: /monkeys/jump
Mapping: MonkeysController->jump();
URL: /products
Mapping: ProductsController->index();
URL: /tasks/view/45
Mapping: TasksController->view(45);
URL: /donations/view/recent/2001
Mapping: DonationsController->view('recent', '2001');
URL: /contents/view/chapter:models/section:associations
Mapping: ContentsController->view();$this->passedArgs['chapter'] = 'models';$this->passedArgs['section'] = 'associations';$this->params['named']['chapter'] = 'models';$this->params['named']['section'] = 'associations';
When making custom routes, a common pitfall is that using named parameters will break your custom routes. In orderto solve this you should inform the Router about which parameters are intended to be named parameters. Without thisknowledge the Router is unable to determine whether named parameters are intended to actually be named parameters orrouted parameters, and defaults to assuming you intended them to be routed parameters. To connect named parametersin the router use Router::connectNamed():Router::connectNamed(array('chapter', 'section'));Will ensure that your chapter and section parameters reverse route correctly.
When generating URLs, using a routing array you add named parameters as values with string keys matching the name:array('controller' => 'posts', 'action' => 'view', 'chapter' => 'association')
Since ‘chapter’ doesn’t match any defined route elements, it’s treated as a named parameter.
Note: Both named parameters and route elements share the same key-space. It’s best to avoid re-using a key for botha route element and a named parameter.

598

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Named parameters also support using arrays to generate and parse URLs. The syntax works very similar to the array
syntax used for GET parameters. When generating URLs you can use the following syntax:$url = Router::url(array('controller' => 'posts','action' => 'index','filter' => array('published' => 1,'frontpage' => 1)));
The above would generate the URL /posts/index/filter[published]:1/filter[frontpage]:1. The parameters are then parsed and stored in your controller’s passedArgs variable as an array, just as you sent them to
Router::url:$this->passedArgs['filter'] = array('published' => 1,'frontpage' => 1);
Arrays can be deeply nested as well, allowing you even more flexibility in passing arguments:$url = Router::url(array('controller' => 'posts','action' => 'search','models' => array('post' => array('order' => 'asc','filter' => array('published' => 1)),
'comment' => array('order' => 'desc','filter' => array('spam' => 0)),),'users' => array(1, 2, 3)));
You would end up with a pretty long url like this (wrapped for easy reading):
posts/search/models[post][order]:asc/models[post][filter][published]:1/models[comment][order]:desc/models[comment][filter][spam]:0/users[]:1/users[]:2/users[]:3
And the resulting array that would be passed to the controller would match that which you passed to the router:$this->passedArgs['models'] = array('post' => array((continues on next page)

Routing599

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'order' => 'asc','filter' => array('published' => 1)),
'comment' => array('order' => 'desc','filter' => array('spam' => 0)),);

Controlling Named Parameters
You can control named parameter configuration at the per-route-level or control them globally. Global control is done
through Router::connectNamed() The following gives some examples of how you can control named parameterparsing with connectNamed().
Do not parse any named parameters:
Router::connectNamed(false);
Parse only default parameters used for CakePHP’s pagination:
Router::connectNamed(false, array('default' => true));
Parse only the page parameter if its value is a number:
Router::connectNamed(array('page' => '[\d]+'),
array('default' => false, 'greedy' => false));
Parse only the page parameter no matter what:
Router::connectNamed(array('page'),array('default' => false, 'greedy' => false));
Parse only the page parameter if the current action is ‘index’:Router::connectNamed(array('page' => array('action' => 'index')),array('default' => false, 'greedy' => false));
Parse only the page parameter if the current action is ‘index’ and the controller is ‘pages’:
Router::connectNamed(array('page' => array('action' => 'index', 'controller' => 'pages')),(continues on next page)

600

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)array('default' => false, 'greedy' => false));
connectNamed() supports a number of options:• greedy Setting this to true will make Router parse all named params. Setting it to false will parse only theconnected named params.
• default Set this to true to merge in the default set of named parameters.
• reset Set to true to clear existing rules and start fresh.
• separator Change the string used to separate the key & value in a named parameter. Defaults to :Reverse Routing
Reverse routing is a feature in CakePHP that is used to allow you to easily change your URL structure without having
to modify all your code. By using routing arrays to define your URLs, you can later configure routes and the generated
URLs will automatically update.
If you create URLs using strings like:$this->Html->link('View', '/posts/view/' . $id);
And then later decide that /posts should really be called ‘articles’ instead, you would have to go through your entire
application renaming URLs. However, if you defined your link like:$this->Html->link('View',
array('controller' => 'posts', 'action' => 'view', $id));
Then when you decided to change your URLs, you could do so by defining a route. This would change both theincoming URL mapping, as well as the generated URLs.
When using array URLs, you can define both query string parameters and document fragments using special keys:
Router::url(array('controller' => 'posts','action' => 'index','?' => array('page' => 1),'#' => 'top'));// will generate a URL like.
/posts/index?page=1#top

Routing

601

CakePHP Cookbook Documentation, Release 2.x

Redirect Routing
Redirect routing allows you to issue HTTP status 30x redirects for incoming routes, and point them at different URLs.
This is useful when you want to inform client applications that a resource has moved and you don’t want to expose two
URLs for the same content
Redirection routes are different from normal routes as they perform an actual header redirection if a match is found.
The redirection can occur to a destination within your application or an outside location:
Router::redirect('/home/*',array('controller' => 'posts', 'action' => 'view'),// or array('persist'=>array('id')) for default routing where the// view action expects $id as an argument
array('persist' => true));
Redirects /home/* to /posts/view and passes the parameters to /posts/view. Using an array as the redirect destination allows you to use other routes to define where a URL string should be redirected to. You can redirect to external
locations using string URLs as the destination:
Router::redirect('/posts/*', 'https://google.com', array('status' => 302));
This would redirect /posts/* to https://google.com with a HTTP status of 302.

Disabling the Default Routes
If you have fully customized all your routes, and want to avoid any possible duplicate content penalties from searchengines, you can remove the default routes that CakePHP offers by deleting them from your application’s routes.php
file.
This will cause CakePHP to serve errors, when users try to visit URLs that would normally be provided by CakePHP
but have not been connected explicitly.

Custom Route Classes
Custom route classes allow you to extend and change how individual routes parse requests and handle reverse routing.
A custom route class should be created in app/Routing/Route and should extend CakeRoute and implement one orboth of match() and/or parse(). parse() is used to parse requests and match() is used to handle reverse routing.
You can use a custom route class when making a route by using the routeClass option, and loading the file containing
your route before trying to use it:
App::uses('SlugRoute', 'Routing/Route');Router::connect('/:slug',array('controller' => 'posts', 'action' => 'view'),array('routeClass' => 'SlugRoute'));
This route would create an instance of SlugRoute and allow you to implement custom parameter handling.

602

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Router API
class Router
Router manages generation of outgoing URLs, and parsing of incoming request uri’s into parameter sets that
CakePHP can dispatch.
static Router::connect($route, $defaults = array(), $options = array())
Parameters• $route (string) – A string describing the template of the route• $defaults (array) – An array describing the default route parameters. These parameters
will be used by default and can supply routing parameters that are not dynamic.
• $options (array) – An array matching the named elements in the route to regular expressions which that element should match. Also contains additional parameters such as whichrouted parameters should be shifted into the passed arguments, supplying patterns for routing
parameters and supplying the name of a custom routing class.
Routes are a way of connecting request URLs to objects in your application. At their core routes are a set of
regular expressions that are used to match requests to destinations.
Examples:Router::connect('/:controller/:action/*');
The first parameter will be used as a controller name while the second is used as the action name. The ‘/*’ syntax
makes this route greedy in that it will match requests like /posts/index as well as requests like /posts/edit/
1/foo/bar .
Router::connect('/home-page',array('controller' => 'pages', 'action' => 'display', 'home'));
The above shows the use of route parameter defaults. And providing routing parameters for a static route.
Router::connect('/:lang/:controller/:action/:id',array(),
array('id' => '[0-9]+', 'lang' => '[a-z]{3}'));Shows connecting a route with custom route parameters as well as providing patterns for those parameters.
Patterns for routing parameters do not need capturing groups, as one will be added for each route params.
$options offers three ‘special’ keys. pass, persist and routeClass have special meaning in the $options
array.
• pass is used to define which of the routed parameters should be shifted into the pass array. Adding aparameter to pass will remove it from the regular route array. Ex. 'pass' => array('slug')• persist is used to define which route parameters should be automatically included when generating new
URLs. You can override persistent parameters by redefining them in a URL or remove them by setting theparameter to false. Ex. 'persist' => array('lang')• routeClass is used to extend and change how individual routes parse requests and handle reverse routing,
via a custom routing class. Ex. 'routeClass' => 'SlugRoute'Routing603

CakePHP Cookbook Documentation, Release 2.x• named is used to configure named parameters at the route level. This key uses the same options asRouter::connectNamed()static Router::redirect($route, $url, $options = array())
Parameters• $route (string) – A route template that dictates which URLs should be redirected.
• $url (mixed) – Either a routing array or a string url for the destination of the redirect.
• $options (array) – An array of options for the redirect.
Connects a new redirection Route in the router. See Redirect Routing for more information.
static Router::connectNamed($named, $options = array())
Parameters• $named (array) – A list of named parameters. Key value pairs are accepted where values
are either regex strings to match, or arrays.
• $options (array) – Allows control of all settings: separator, greedy, reset, default
Specifies what named parameters CakePHP should be parsing out of incoming URLs. By default CakePHP willparse every named parameter out of incoming URLs. See Controlling Named Parameters for more information.
static Router::promote($which = null)Parameters• $which (integer) – A zero-based array index representing the route to move. For example,
if 3 routes have been added, the last route would be 2.
Promote a route (by default, the last one added) to the beginning of the list.
static Router::url($url = null, $full = false)
Parameters• $url (mixed) – Cake-relative URL, like “/products/edit/92” or “/presidents/elect/4” or arouting array• $full (mixed) – If (boolean) true, the full base URL will be prepended to the result. If anarray accepts the following keys
• escape - used when making URLs embedded in HTML escapes query string ‘&’• full - if true the full base URL will be prepended.
Generate a URL for the specified action. Returns a URL pointing to a combination of controller and action. $url
can be:• Empty - the method will find the address to the actual controller/action.
• ‘/’ - the method will find the base URL of application.
• A combination of controller/action - the method will find the URL for it.
There are a few ‘special’ parameters that can change the final URL string that is generated:• base - Set to false to remove the base path from the generated URL. If your application is not in the root
directory, this can be used to generate URLs that are ‘CakePHP relative’. CakePHP relative URLs are
required when using requestAction.
• ? - Takes an array of query string parameters604

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x• # - Allows you to set URL hash fragments.
• full_base - If true the value of Router::fullBaseUrl() will be prepended to generated URLs.
static Router::mapResources($controller, $options = array())
Creates REST resource routes for the given controller(s). See the REST section for more information.
static Router::parseExtensions($types)
Used in routes.php to declare which File Extensions your application supports. By providing no arguments, allfile extensions will be supported.
static Router::setExtensions($extensions, $merge = true)
New in version 2.2.
Set or add valid extensions.
Router::parseExtensions().

To have the extensions parsed, you are still required to callstatic Router::defaultRouteClass($classname)
New in version 2.1.
Set the default route to be used when connecting routes in the future.
static Router::fullBaseUrl($url = null)
New in version 2.4.
Get or set the baseURL used for generating URL’s. When setting this value you should be sure to include thefully qualified domain name including protocol.
Setting values with this method will also update App.fullBaseUrl in Configure.
class CakeRouteThe base class for custom routes to be based on.
CakeRoute::parse($url)
Parameters• $url (string) – The string URL to parse.
Parses an incoming URL, and generates an array of request parameters that Dispatcher can act upon. Extending
this method allows you to customize how incoming URLs are converted into an array. Return false from URL
to indicate a match failure.
CakeRoute::match($url)
Parameters• $url (array) – The routing array to convert into a string URL.
Attempt to match a URL array. If the URL matches the route parameters and settings, then return a generated
string URL. If the URL doesn’t match the route parameters, false will be returned. This method handles thereverse routing or conversion of URL arrays into string URLs.
CakeRoute::compile()
Force a route to compile its regular expression.

Routing605

CakePHP Cookbook Documentation, Release 2.x

Sessions
CakePHP provides a wrapper and suite of utility features on top of PHP’s native session extension. Sessions allow
you to identify unique users across the requests and store persistent data for specific users. Unlike Cookies, session
data is not available on the client side. Usage of $_SESSION is generally avoided in CakePHP, and instead usage of theSession classes is preferred.

Session Configuration
Session configuration is stored in Configure under the top level Session key, and a number of options are available:• Session.cookie - Change the name of the session cookie.
• Session.timeout - The number of minutes before CakePHP’s session handler expires the session. This affects
Session.autoRegenerate (below), and is handled by CakeSession.
• Session.cookieTimeout - The number of minutes before the session cookie expires. If this is undefined, it
will use the same value as Session.timeout. This affects the session cookie, and is handled by PHP itself.
• Session.checkAgent - Should the user agent be checked, on each request. If the user agent does not match thesession will be destroyed.
• Session.autoRegenerate - Enabling this setting, turns on automatic renewal of sessions, and session ids thatchange frequently. Enabling this value will use the session’s Config.countdown value to keep track of requests.
Once the countdown reaches 0, the session id will be regenerated. This is a good option to use for applications
that need frequently changing session ids for security reasons. You can control the number of requests needed
to regenerate the session by modifying CakeSession::$requestCountdown.
• Session.defaults - Allows you to use one the built-in default session configurations as a base for your session
configuration.
• Session.handler - Allows you to define a custom session handler. The core database and cache session handlers use this. This option replaces Session.save in previous versions. See below for additional information
on Session handlers.
• Session.ini - Allows you to set additional session ini settings for your config. This combined with Session.
handler replace the custom session handling features of previous versions• Session.cacheLimiter - Allows you define the cache control headers used for the session cookie. The default
is must-revalidate. This option was added in 2.8.0.
CakePHP’s defaults to setting session.cookie_secure to true, when your application is on an SSL protocol. If yourapplication serves from both SSL and non-SSL protocols, then you might have problems with sessions being lost. If
you need access to the session on both SSL and non-SSL domains you will want to disable this:Configure::write('Session', array('defaults' => 'php','ini' => array('session.cookie_secure' => false)));
Session cookie paths default to / in 2.0, to change this you can use the session.cookie_path ini flag to the directory
path of your application:
Configure::write('Session', array('defaults' => 'php',(continues on next page)

606

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'ini' => array('session.cookie_path' => '/app/dir')));If you are using php’s default session settings, take note that session.gc_maxlifetime can override your setting fortimeout. The default is 24 minutes. Change this in your ini settings to get it to match longer sessions:
Configure::write('Session', array('defaults' => 'php','timeout' => 2160, // 36 hours'ini' => array('session.gc_maxlifetime' => 129600 // 36 hours)));

Built-in Session handlers & configuration
CakePHP comes with several built-in session configurations. You can either use these as the basis for your session
configuration, or you can create a fully custom solution. To use defaults, simply set the ‘defaults’ key to the name ofthe default you want to use. You can then override any sub setting by declaring it in your Session config:
Configure::write('Session', array('defaults' => 'php'));
The above will use the built-in ‘php’ session configuration. You could augment part or all of it by doing the following:
Configure::write('Session', array('defaults' => 'php','cookie' => 'my_app','timeout' => 4320 //3 days));
The above overrides the timeout and cookie name for the ‘php’ session configuration. The built-in configurations are:• php - Saves sessions with the standard settings in your php.ini file.
• cake - Saves sessions as files inside app/tmp/sessions. This is a good option when on hosts that don’t allow
you to write outside your own home dir.
• database - Use the built-in database sessions. See below for more information.
• cache - Use the built-in cache sessions. See below for more information.

Sessions607

CakePHP Cookbook Documentation, Release 2.x

Session Handlers
Session handlers can also be defined in the session config array. When defined they allow you to map the various session_save_handler values to a class or object you want to use for session saving. There are two
ways to use the ‘handler’. The first is to provide an array with 5 callables. These callables are then applied tosession_set_save_handler:
Configure::write('Session', array('userAgent' => false,'cookie' => 'my_cookie','timeout' => 600,
'handler' => array(
array('Foo', 'open'),array('Foo', 'close'),array('Foo', 'read'),array('Foo', 'write'),array('Foo', 'destroy'),array('Foo', 'gc'),),'ini' => array('cookie_secure' => 1,'use_trans_sid' => 0)));
The second mode is to define an ‘engine’ key.
This key should be a class name that implements
CakeSessionHandlerInterface. Implementing this interface will allow CakeSession to automatically map the
methods for the handler. Both the core Cache and Database session handlers use this method for saving sessions. Additional settings for the handler should be placed inside the handler array. You can then read those values out frominside your handler.
You can also use session handlers from inside plugins. By setting the engine to something like MyPlugin.
PluginSessionHandler. This will load and use the PluginSessionHandler class from inside the MyPlugin ofyour application.
CakeSessionHandlerInterface
This interface is used for all custom session handlers inside CakePHP, and can be used to create custom user landsession handlers. Simply implement the interface in your class and set Session.handler.engine to the classname you’ve created. CakePHP will attempt to load the handler from inside app/Model/Datasource/Session/$classname.php. So if your class name is AppSessionHandler the file should be app/Model/Datasource/
Session/AppSessionHandler.php.
Database sessions
The changes in session configuration change how you define database sessions. Most of the time you will only need to
set Session.handler.model in your configuration as well as choose the database defaults:
Configure::write('Session', array('defaults' => 'database','handler' => array('model' => 'CustomSession'(continues on next page)

608

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x(continued from previous page))));The above will tell CakeSession to use the built-in ‘database’ defaults, and specify that a model called CustomSession
will be the delegate for saving session information to the database.
If you do not need a fully custom session handler, but still require database-backed session storage, you can simplify
the above code to:
Configure::write('Session', array('defaults' => 'database'));This configuration will require a database table to be added with at least these fields:
CREATE TABLE `cake_sessions` (`id` varchar(255) NOT NULL DEFAULT '',`data` text,`expires` int(11) DEFAULT NULL,PRIMARY KEY (`id`));
You can also use the schema shell to create this table using the schema file provided in the default app skeleton:$ Console/cake schema create sessions

Cache Sessions
The Cache class can be used to store sessions as well. This allows you to store sessions in a cache like APC, memcache,or Xcache. There are some caveats to using cache sessions, in that if you exhaust the cache space, sessions will start toexpire as records are evicted.
To use Cache based sessions you can configure you Session config like:
Configure::write('Session', array('defaults' => 'cache','handler' => array('config' => 'session')));
This will configure CakeSession to use the CacheSession class as the delegate for saving the sessions. You can use
the ‘config’ key which cache configuration to use. The default cache configuration is 'default'.

Sessions609

CakePHP Cookbook Documentation, Release 2.x

Setting ini directives
The built-in defaults attempt to provide a common base for session configuration. You may need to tweak specificini flags as well. CakePHP exposes the ability to customize the ini settings for both default configurations, as well as
custom ones. The ini key in the session settings, allows you to specify individual configuration values. For example
you can use it to control settings like session.gc_divisor:
Configure::write('Session', array('defaults' => 'php','ini' => array('session.gc_divisor' => 1000,'session.cookie_httponly' => true)));

Creating a custom session handler
Creating a custom session handler is straightforward in CakePHP. In this example we’ll create a session handler that
stores sessions both in the Cache (apc) and the database. This gives us the best of fast IO of apc, without having toworry about sessions evaporating when the cache fills up.
First we’ll need to create our custom class and put it in app/Model/Datasource/Session/ComboSession.php.
The class should look something like:
App::uses('DatabaseSession', 'Model/Datasource/Session');class ComboSession extends DatabaseSession implements CakeSessionHandlerInterface {public $cacheKey;
public function __construct() {$this->cacheKey = Configure::read('Session.handler.cache');parent::__construct();}// read data from the session.
public function read($id) {$result = Cache::read($id, $this->cacheKey);
if ($result) {return $result;}
return parent::read($id);}// write data into the session.
public function write($id, $data) {Cache::write($id, $data, $this->cacheKey);
return parent::write($id, $data);}// destroy a session.
public function destroy($id) {Cache::delete($id, $this->cacheKey);
return parent::destroy($id);(continues on next page)

610

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}// removes expired sessions.
public function gc($expires = null) {Cache::gc($this->cacheKey);
return parent::gc($expires);}}Our class extends the built-in DatabaseSession so we don’t have to duplicate all of its logic and behavior. We wrap
each operation with a Cache operation. This lets us fetch sessions from the fast cache, and not have to worry about
what happens when we fill the cache. Using this session handler is also easy. In your core.php make the session block
look like the following:
Configure::write('Session', array('defaults' => 'database','handler' => array('engine' => 'ComboSession','model' => 'Session','cache' => 'apc')));// Make sure to add a apc cache config
Cache::config('apc', array('engine' => 'Apc'));Now our application will start using our custom session handler for reading & writing session data.
class CakeSession

Reading & writing session data
Depending on the context you are in, your application has different classes that provide access to the session. In
controllers you can use SessionComponent. In the view, you can use SessionHelper. In any part of your application
you can use CakeSession to access the session as well. Like the other interfaces to the session, CakeSession provides
a simple CRUD interface.
static CakeSession::read($key)
You can read values from the session using Set::classicExtract() compatible syntax:
CakeSession::read('Config.language');static CakeSession::write($key, $value)$key should be the dot separated path you wish to write $value to:
CakeSession::write('Config.language', 'eng');
static CakeSession::delete($key)
When you need to delete data from the session, you can use delete:Sessions611

CakePHP Cookbook Documentation, Release 2.x

CakeSession::delete('Config.language');
You should also see the documentation on Sessions and SessionHelper for how to access Session data in the controller
and view.

Exceptions
Exceptions can be used for a variety of uses in your application. CakePHP uses exceptions internally to indicate logic
errors or misuse. All of the exceptions CakePHP raises extend CakeException, and there are class/task specificexceptions that extend this base class.
CakePHP also provides a number of exception classes that you can use for HTTP errors. See the section on Built-in
Exceptions for CakePHP for more information.

Exception configuration
There are a few keys available for configuring exceptions:
Configure::write('Exception', array('handler' => 'ErrorHandler::handleException','renderer' => 'ExceptionRenderer','log' => true));• handler - callback - The callback to handle exceptions. You can set this to any callback type, including anonymous functions.
• renderer - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you
should place the file for that class in app/Lib/Error. This class needs to implement a render() method.
• log - boolean - When true, exceptions + their stack traces will be logged to CakeLog.
• consoleHandler - callback - The callback used to handle exceptions, in a console context. If undefined,
CakePHP’s default handler will be used.
Exception rendering by default displays an HTML page, you can customize either the handler or the renderer by changing the settings. Changing the handler, allows you to take full control over the exception handling process, while changing the renderer allows you to easily change the output type/contents, as well as add in application specific exception
handling.
New in version 2.2: The Exception.consoleHandler option was added in 2.2.

Exception classes
There are a number of exception classes in CakePHP. Each exception replaces a cakeError() error messages from thepast. Exceptions offer additional flexibility in that they can be extended and contain some logic. The built in exception
handling will capture any uncaught exceptions and render a useful page. Exceptions that do not specifically use a 400
range code, will be treated as an Internal Server Error.

612

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Built-in Exceptions for CakePHP
There are several built-in exceptions inside CakePHP, outside of the internal framework exceptions, there are several
exceptions for HTTP methods
exception BadRequestException
Used for doing 400 Bad Request error.
exception UnauthorizedException
Used for doing a 401 Unauthorized error.
exception ForbiddenException
Used for doing a 403 Forbidden error.
exception NotFoundException
Used for doing a 404 Not found error.
exception MethodNotAllowedException
Used for doing a 405 Method Not Allowed error.
exception InternalErrorException
Used for doing a 500 Internal Server Error.
exception NotImplementedException
Used for doing a 501 Not Implemented Errors.
You can throw these exceptions from your controllers to indicate failure states, or HTTP errors. An example use of the
HTTP exceptions could be rendering 404 pages for items that have not been found:public function view($id) {$post = $this->Post->findById($id);if (!$post) {throw new NotFoundException('Could not find that post');}$this->set('post', $post);}
By using exceptions for HTTP errors, you can keep your code both clean, and give RESTful responses to client applications and users.
In addition, the following framework layer exceptions are available, and will be thrown from a number of CakePHP
core components:
exception CakeException
Base exception class in CakePHP. All framework layer exceptions thrown by CakePHP will extend this class.
These exception classes all extend CakeException. By extending CakeException, you can create your own ‘framework’ errors. All of the standard Exceptions that CakePHP will throw also extend CakeException.
New in version 2.3: CakeBaseException was added
exception CakeBaseException
Base exception class in CakePHP. All CakeExceptions and HttpExceptions above extend this class.
CakeBaseException::responseHeader($header = null, $value = null)
See CakeResponse::header()All Http and CakePHP exceptions extend the CakeBaseException class, which has a method to add headers to the
response. For instance when throwing a 405 MethodNotAllowedException the rfc2616 says: “The response MUST
include an Allow header containing a list of valid methods for the requested resource.”
Exceptions

613

CakePHP Cookbook Documentation, Release 2.x

exception MissingViewException
The chosen view file could not be found.
exception MissingLayoutException
The chosen layout could not be found.
exception MissingHelperException
A helper was not found.
exception MissingBehaviorException
A configured behavior could not be found.
exception MissingComponentException
A configured component could not be found.
exception MissingTaskException
A configured task was not found.
exception MissingShellException
The shell class could not be found.
exception MissingShellMethodException
The chosen shell class has no method of that name.
exception MissingDatabaseException
The configured database is missing.
exception MissingConnectionException
A model’s connection is missing.
exception MissingTableException
A model’s table is missing from CakePHP’s cache or the datasource. Upon adding a new table to a datasource,
the model cache (found in tmp/cache/models by default) must be removed.
exception MissingActionException
The requested controller action could not be found.
exception MissingControllerException
The requested controller could not be found.
exception PrivateActionException
Private action access. Either accessing private/protected/_ prefixed actions, or trying to access prefixed routes
incorrectly.

Using HTTP exceptions in your controllers
You can throw any of the HTTP related exceptions from your controller actions to indicate failure states. For example:
public function view($id) {$post = $this->Post->findById($id);if (!$post) {throw new NotFoundException();}$this->set(compact('post'));}
The above would cause the configured Exception.handler to catch and process the NotFoundException. Bydefault this will create an error page, and log the exception.
614

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Exception Renderer
class ExceptionRenderer(Exception $exception)
The ExceptionRenderer class with the help of CakeErrorController takes care of rendering the error pages for all
the exceptions thrown by you application.
The error page views are located at app/View/Errors/. For all 4xx and 5xx errors the view files error400.ctp anderror500.ctp are used respectively. You can customize them as per your needs. By default your app/Layouts/
default.ctp is used for error pages too. If for eg. you want to use another layout app/Layouts/my_error.ctp
for your error pages, then simply edit the error views and add the statement $this->layout = 'my_error'; to theerror400.ctp and error500.ctp.
Each framework layer exception has its own view file located in the core templates but you really don’t need to bothercustomizing them as they are used only during development. With debug turned off all framework layer exceptions areconverted to InternalErrorException.

Creating your own application exceptions
You can create your own application exceptions using any of the built in SPL exceptions113 , Exception itself, or
CakeException. Application exceptions that extend Exception or the SPL exceptions will be treated as 500 error
in production mode. CakeException is special in that all CakeException objects are coerced into either 500 or
404 errors depending on the code they use. When in development mode CakeException objects simply need a new
template that matches the class name in order to provide useful information. If your application contained the following
exception:
class MissingWidgetException extends CakeException {};You could provide nice development errors, by creating app/View/Errors/missing_widget.ctp. When in production mode, the above error would be treated as a 500 error. The constructor for CakeException has been extended,allowing you to pass in hashes of data. These hashes are interpolated into the the messageTemplate, as well as into theview that is used to represent the error in development mode. This allows you to create data rich exceptions, by providing more context for your errors. You can also provide a message template which allows the native __toString()
methods to work as normal:
class MissingWidgetException extends CakeException {protected $_messageTemplate = 'Seems that %s is missing.';}
throw new MissingWidgetException(array('widget' => 'Pointy'));When caught by the built-in exception handler, you would get a $widget variable in your error view template. In
addition if you cast the exception as a string or use its getMessage() method you will get Seems that Pointy
is missing.. This allows you easily and quickly create your own rich development errors, just like CakePHP uses
internally.
113 https://www.php.net/manual/en/spl.exceptions.php

Exceptions

615

CakePHP Cookbook Documentation, Release 2.x

Creating custom status codes
You can create custom HTTP status codes by changing the code used when creating an exception:
throw new MissingWidgetHelperException('Its not here', 501);
Will create a 501 response code, you can use any HTTP status code you want. In development, if your exception doesn’t
have a specific template, and you use a code equal to or greater than 500 you will see the error500 template. For any
other error code you’ll get the error400 template. If you have defined an error template for your custom exception,that template will be used in development mode. If you’d like your own exception handling logic even in production,
see the next section.

Extending and implementing your own Exception handlers
You can implement application specific exception handling in one of a few ways. Each approach gives you different
amounts of control over the exception handling process.
• Set Configure::write('Exception.handler', 'YourClass::yourMethod');• Create AppController::appError();• Set Configure::write('Exception.renderer', 'YourClass');
In the next few sections, we will detail the various approaches and the benefits each has.

Create your own Exception handler with Exception.handler
Creating your own exception handler gives you full control over the exception handling process. The class you choose
should be loaded in your app/Config/bootstrap.php, so it’s available to handle any exceptions. You can define thehandler as any callback type. By settings Exception.handler CakePHP will ignore all other Exception settings. A
sample custom exception handling setup could look like:// in app/Config/core.php
Configure::write('Exception.handler', 'AppExceptionHandler::handle');// in app/Config/bootstrap.php
App::uses('AppExceptionHandler', 'Lib');// in app/Lib/AppExceptionHandler.php
class AppExceptionHandler {public static function handle($error) {echo 'Oh noes! ' . $error->getMessage();// ...
}// ...
}
You can run any code you wish inside handleException. The code above would simple print ‘Oh noes! ‘ plus the
exception message. You can define exception handlers as any type of callback, even an anonymous function if you areusing PHP 5.3:
Configure::write('Exception.handler', function ($error) {echo 'Ruh roh ' . $error->getMessage();
});

616

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

By creating a custom exception handler you can provide custom error handling for application exceptions. In the method
provided as the exception handler you could do the following:// in app/Lib/AppErrorHandler.php
class AppErrorHandler {public static function handleException($error) {if ($error instanceof MissingWidgetException) {return self::handleMissingWidget($error);}// do other stuff.
}}

Using AppController::appError()
Implementing this method is an alternative to implementing a custom exception handler. It’s primarily provided forbackwards compatibility, and is not recommended for new applications. This controller method is called instead of thedefault exception rendering. It receives the thrown exception as its only argument. You should implement your error
handling in that method:class AppController extends Controller {public function appError($error) {// custom logic goes here.
}}

Using a custom renderer with Exception.renderer to handle application exceptions
If you don’t want to take control of the exception handling, but want to change how exceptions are rendered you can use
Configure::write('Exception.renderer', 'AppExceptionRenderer'); to choose a class that will render
exception pages. By default :php:class`ExceptionRenderer` is used. Your custom exception renderer class should beplaced in app/Lib/Error. Or an Error directory in any bootstrapped Lib path. In a custom exception rendering classyou can provide specialized handling for application specific errors:// in app/Lib/Error/AppExceptionRenderer.php
App::uses('ExceptionRenderer', 'Error');class AppExceptionRenderer extends ExceptionRenderer {public function missingWidget($error) {echo 'Oops that widget is missing!';}}
The above would handle any exceptions of the type MissingWidgetException, and allow you to provide custom
display/handling logic for those application exceptions. Exception handling methods get the exception being handled
as their argument.
Note: Your custom renderer should expect an exception in its constructor, and implement a render method. Failing to
do so will cause additional errors.

Exceptions

617

CakePHP Cookbook Documentation, Release 2.x

Note: If you are using a custom Exception.handler this setting will have no effect. Unless you reference it inside
your implementation.

Creating a custom controller to handle exceptions
In your ExceptionRenderer sub-class, you can use the _getController method to allow you to return a custom controller to handle your errors. By default CakePHP uses CakeErrorController which omits a few of the normal
callbacks to help ensure errors always display. However, you may need a more custom error handling controller in yourapplication. By implementing _getController in your AppExceptionRenderer class, you can use any controller
you want:
class AppExceptionRenderer extends ExceptionRenderer {protected function _getController($exception) {App::uses('SuperCustomErrorController', 'Controller');
return new SuperCustomErrorController();}}
Alternatively, you could just override the core CakeErrorController, by including one in app/Controller. If you areusing a custom controller for error handling, make sure you do all the setup you need in your constructor, or the render
method. As those are the only methods that the built-in ErrorHandler class directly call.
Logging exceptions
Using the built-in exception handling, you can log all the exceptions that are dealt with by ErrorHandler by setting
Exception.log to true in your core.php. Enabling this will log every exception to CakeLog and the configured
loggers.
Note: If you are using a custom Exception.handler this setting will have no effect. Unless you reference it inside
your implementation.

Error Handling
For 2.0 Object::cakeError() has been removed. Instead it has been replaced with a number of exceptions. All of
the core classes that previously called cakeError are now throwing exceptions. This lets you either choose to handle
the errors in your application code, or let the built-in exception handling deal with them.
There is more control than ever for error and exception handling in CakePHP 2.0. You can configure which methods
you want to set as the default error handler, and exception handler using configure.

618

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Error configuration
Error configuration is done inside your application’s app/Config/core.php file. You can define a callback to be fired
each time your application triggers any PHP error. Exceptions are handled separately. The callback can be any PHP
callable, including an anonymous function. The default error handling configuration looks like:
Configure::write('Error', array('handler' => 'ErrorHandler::handleError','level' => E_ALL & ~E_DEPRECATED,'trace' => true));
You have 5 built-in options when configuring error handlers:• handler - callback - The callback to handle errors. You can set this to any callable type, including anonymous
functions.
• level - int - The level of errors you are interested in capturing. Use the built-in PHP error constants, andbitmasks to select the level of error you are interested in.
• trace - boolean - Include stack traces for errors in log files. Stack traces will be included in the log after eacherror. This is helpful for finding where/when errors are being raised.
• consoleHandler - callback - The callback used to handle errors when running in the console. If undefined,
CakePHP’s default handlers will be used.
ErrorHandler by default, displays errors when debug > 0, and logs errors when debug = 0. The type of errors captured
in both cases is controlled by Error.level. The fatal error handler will be called independent of debug level orError.level configuration, but the result will be different based on debug level.
Note: If you use a custom error handler, the trace setting will have no effect, unless you refer to it in your error handling
function.
New in version 2.2: The Error.consoleHandler option was added in 2.2.
Changed in version 2.2: The Error.handler and Error.consoleHandler will receive the fatal error codes as well.
The default behavior is show a page to internal server error (debug disabled) or a page with the message, file and line
(debug enabled).

Creating your own error handler
You can create an error handler out of any callback type. For example you could use a class called AppError to handle
your errors. The following would need to be done://in app/Config/core.php
Configure::write('Error.handler', 'AppError::handleError');//in app/Config/bootstrap.php
App::uses('AppError', 'Lib');//in app/Lib/AppError.php
class AppError {public static function handleError($code, $description, $file = null,$line = null, $context = null) {echo 'There has been an error!';(continues on next page)

Error Handling619

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}}
This class/method will print out ‘There has been an error!’ each time an error occurs. Since you can define an error
handler as any callback type, you could use an anonymous function if you are using PHP5.3 or greater.
Configure::write('Error.handler', function($code, $description, $file = null,$line = null, $context = null) {echo 'Oh no something bad happened';});
It is important to remember that errors captured by the configured error handler will be php errors, and that if you needcustom error handling, you probably also want to configure Exceptions handling as well.

Changing fatal error behavior
Since CakePHP 2.2 the Error.handler will receive the fatal error codes as well. If you do not want to show the cakeerror page, you can override it like://in app/Config/core.php
Configure::write('Error.handler', 'AppError::handleError');//in app/Config/bootstrap.php
App::uses('AppError', 'Lib');//in app/Lib/AppError.php
class AppError {public static function handleError($code, $description, $file = null,$line = null, $context = null) {list(, $level) = ErrorHandler::mapErrorCode($code);if ($level === LOG_ERR) {// Ignore fatal error. It will keep the PHP error message only
return false;}
return ErrorHandler::handleError($code,$description,$file,$line,$context);}}
If you want to keep the default fatal error behavior, you can call ErrorHandler::handleFatalError() from yourcustom handler.

620

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Debugging
Debugging is an inevitable and necessary part of any development cycle. While CakePHP doesn’t offer any tools that
directly connect with any IDE or editor, CakePHP does provide several tools to assist in debugging and exposing what
is running under the hood of your application.

Basic Debugging
debug(mixed $var, boolean $showHtml = null, $showFrom = true)
Parameters• $var (mixed) – The contents to print out. Arrays and objects work well.
• $showHTML (boolean) – Set to true, to enable escaping. Escaping is enabled by default in
2.0 when serving web requests.
• $showFrom (boolean) – Show the line and file the debug() occurred on.
The debug() function is a globally available function that works similarly to the PHP function print_r(). The debug()
function allows you to show the contents of a variable in a number of different ways. First, if you’d like data to beshown in an HTML-friendly way, set the second parameter to true. The function also prints out the line and file it isoriginating from by default.
Output from this function is only shown if the core debug variable has been set to a value greater than 0.
Changed in version 2.1: The output of debug() more resembles var_dump(), and uses Debugger internally.

Debugger ClassThe debugger class was introduced with CakePHP 1.2 and offers even more options for obtaining debugging information. It has several functions which are invoked statically, and provide dumping, logging, and error handling functions.
The Debugger Class overrides PHP’s default error handling, replacing it with far more useful error reports. The Debugger’s error handling is used by default in CakePHP. As with all debugging functions, Configure::debug must be
set to a value higher than 0.
When an error is raised, Debugger both outputs information to the page and makes an entry in the error.log file. The
error report that is generated has both a stack trace and a code excerpt from where the error was raised. Click on the“Error” link to reveal the stack trace, and on the “Code” link to reveal the error-causing lines.

Using the Debugger Class
class Debugger
To use the debugger, first ensure that Configure::read(‘debug’) is set to a value greater than 0.
static Debugger::dump($var, $depth = 3)
Dump prints out the contents of a variable. It will print out all properties and methods (if any) of the supplied
variable:$foo = array(1,2,3);
Debugger::dump($foo);(continues on next page)

Debugging621

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// outputs
array(
1,
2,
3)// simple object$car = new Car();Debugger::dump($car);// outputs
Car
Car::colour = 'red'
Car::make = 'Toyota'
Car::model = 'Camry'
Car::mileage = '15000'
Car::accelerate()
Car::decelerate()
Car::stop()
Changed in version 2.1: In 2.1 forward the output was updated for readability. See Debugger::exportVar()
Changed in version 2.5.0: The depth parameter was added.
static Debugger::log($var, $level = 7, $depth = 3)
Creates a detailed stack trace log at the time of invocation. The log() method prints out data similar to that done
by Debugger::dump(), but to the debug.log instead of the output buffer. Note your app/tmp directory (and its
contents) must be writable by the web server for log() to work correctly.
Changed in version 2.5.0: The depth parameter was added.
static Debugger::trace($options)
Returns the current stack trace. Each line of the trace includes the calling method, including which file and line
the call originated from.
//In PostsController::index()pr(Debugger::trace());//outputs
PostsController::index() - APP/Controller/DownloadsController.php, line 48
Dispatcher::_invoke() - CORE/lib/Cake/Routing/Dispatcher.php, line 265
Dispatcher::dispatch() - CORE/lib/Cake/Routing/Dispatcher.php, line 237[main] - APP/webroot/index.php, line 84
Above is the stack trace generated by calling Debugger::trace() in a controller action. Reading the stack trace
bottom to top shows the order of currently running functions (stack frames). In the above example, index.php
called Dispatcher::dispatch(), which in-turn called Dispatcher::_invoke(). The _invoke() method then called
PostsController::index(). This information is useful when working with recursive operations or deep stacks, as
it identifies which functions are currently running at the time of the trace().
static Debugger::excerpt($file, $line, $context)
Grab an excerpt from the file at $path (which is an absolute filepath), highlights line number $line with $context
number of lines around it.
622

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

pr(Debugger::excerpt(ROOT . DS . LIBS . 'debugger.php', 321, 2));//will output the following.
Array([0] => <code><span style="color: #000000"> * @access public</span></code>[1] => <code><span style="color: #000000"> */</span></code>[2] => <code><span style="color: #000000">function excerpt($file, $line,
˓→$context = 2) {</span></code>[3] => <span class="code-highlight"><code><span style="color: #000000">$data = $lines = array();</span></code></span>[4] => <code><span style="color: #000000">$data = @explode("\n", file_
˓→get_contents($file));</span></code>)˓→

Although this method is used internally, it can be handy if you’re creating your own error messages or log entries
for custom situations.
static Debugger::exportVar($var, $recursion = 0)
Converts a variable of any type to a string for use in debug output. This method is also used by most of Debugger
for internal variable conversions, and can be used in your own Debuggers as well.
Changed in version 2.1: This function generates different output in 2.1 forward.
static Debugger::invoke($debugger)
Replace the CakePHP Debugger with a new instance.
static Debugger::getType($var)
Get the type of a variable. Objects will return their class name
New in version 2.1.

Using Logging to debug
Logging messages is another good way to debug applications, and you can use CakeLog to do logging in your application. All objects that extend Object have an instance method log() which can be used to log messages:$this->log('Got here', 'debug');
The above would write Got here into the debug log. You can use log entries to help debug methods that involve
redirects or complicated loops. You can also use CakeLog::write() to write log messages. This method can becalled statically anywhere in your application anywhere CakeLog has been loaded:// In app/Config/bootstrap.php
App::uses('CakeLog', 'Log');// Anywhere in your application
CakeLog::write('debug', 'Got here');Debugging623

CakePHP Cookbook Documentation, Release 2.x

Debug Kit
DebugKit is a plugin that provides a number of good debugging tools. It primarily provides a toolbar in the renderedHTML, that provides a plethora of information about your application and the current request. You can download
DebugKit114 from GitHub.

XdebugIf your environment supplies the Xdebug PHP extension, fatal errors will show additional Xdebug stack trace details.
Details about Xdebug can be found at Xdebug115 .

Testing
CakePHP comes with comprehensive testing support built-in. CakePHP comes with integration for PHPUnit116 . In
addition to the features offered by PHPUnit, CakePHP offers some additional features to make testing easier. Thissection will cover installing PHPUnit, and getting started with Unit Testing, and how you can use the extensions that
CakePHP offers.

Installing PHPUnit
CakePHP uses PHPUnit as its underlying test framework. PHPUnit is the de-facto standard for unit testing in PHP. It
offers a deep and powerful set of features for making sure your code does what you think it does.
Install via Composer
For a long time, CakePHP 2.x supported PHPunit 3.7.x only. To install PHPUnit as a development dependency throughComposer, execute the following in the same directory as your composer.json:
php composer.phar require --dev phpunit/phpunit:"3.7.38"
As of CakePHP 2.10.0, basic support for PHPUnit 4.x and 5.x was added. To upgrade PHPUnit and its dependencies
for your application, execute the following:
php composer.phar require --dev phpunit/phpunit:"4.* || 5.*" --update-with-dependencies
This will install either PHPUnit 4.x or 5.x, depending on your system setup and your composer.json configuration.
To be able to work with PHP 7, it is required to install the version 5, and you’ll need to setup the autoloader, and workaround an issue in Composer’s autoloader. In your Config/bootstrap.php file add the following:// Load Composer autoload.
require APP . 'Vendor/autoload.php';// Remove and re-prepend CakePHP's autoloader as Composer thinks it is the// most important.
// See: http://goo.gl/kKVJO7
spl_autoload_unregister(array('App', 'load'));spl_autoload_register(array('App', 'load'), true, true);
114 https://github.com/cakephp/debug_kit/tree/2.2
115 https://xdebug.org
116 https://phpunit.de624

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Install via .phar Package
You can also download the file directly. Just make sure you get the correct version from https://phar.phpunit.de/. Make
sure /usr/local/bin is in your php.ini file’s include_path:
wget https://phar.phpunit.de/phpunit-3.7.38.phar -O phpunit.phar
chmod +x phpunit.phar
mv phpunit.phar /usr/local/bin/phpunit

Note: PHPUnit 4 is not compatible with CakePHP’s Unit Testing.
Depending on your system’s configuration, you may need to run the previous commands with sudo

Note: In CakePHP 2.5.7 and later you can place the phar directly in your vendors or App/Vendor directory.

Tip: All output is swallowed when using PHPUnit 3.6+. Add the --debug modifier if using the CLI or add &debug=1
to the URL if using the web runner to display output.

Test Database Setup
Remember to have a debug level of at least 1 in your app/Config/core.php file before running any tests. Tests are
not accessible via the web runner when debug is equal to 0. Before running any tests you should be sure to add a $test
database configuration. This configuration is used by CakePHP for fixture tables and data:
public $test = array('datasource' => 'Database/Mysql','persistent' => false,'host'=> 'dbhost','login'=> 'dblogin','password'=> 'dbpassword','database'=> 'test_database');Note: It’s a good idea to make the test database and your actual database different databases. This will prevent any
embarrassing mistakes later.

Checking the Test Setup
After installing PHPUnit and setting up your $test database configuration you can make sure you’re ready to write andrun your own tests by running one of the core tests. There are two built-in runners for testing, we’ll start off by using
the web runner. The tests can then be accessed by browsing to http://localhost/your_app/test.php. You should see a listof the core test cases. Click on the ‘AllConfigure’ test. You should see a green bar with some additional information
about the tests run, and number passed.
Congratulations, you are now ready to start writing tests!

Testing625

CakePHP Cookbook Documentation, Release 2.x

Test Case Conventions
Like most things in CakePHP, test cases have some conventions. Concerning tests:
1. PHP files containing tests should be in your app/Test/Case/[Type] directories.
2. The filenames of these files should end in Test.php instead of just .php.
3. The classes containing tests
PHPUnit_Framework_TestCase.

shouldextendCakeTestCase,

ControllerTestCaseor

4. Like other class names, the test case class names should match the filename. RouterTest.php should containclass RouterTest extends CakeTestCase.
5. The name of any method containing a test (i.e. containing an assertion) should begin with test, as in
testPublished(). You can also use the @test annotation to mark methods as test methods.
When you have created a test case, you can execute it by browsing to http://localhost/your_app/test.php(depending on how your specific setup looks). Click App test cases, and then click the link to your specific file. You
can run tests from the command line using the test shell:./Console/cake test app Model/Post
For example, would run the tests for your Post model.

Creating Your First Test CaseIn the following example, we’ll create a test case for a very simple helper method. The helper we’re going to test will
be formatting progress bar HTML. Our helper looks like:class ProgressHelper extends AppHelper {public function bar($value) {$width = round($value / 100, 2) * 100;
return sprintf('<div class="progress-container"><div class="progress-bar" style="width: %s%%"></div></div>', $width);}}
This is a very simple example, but it will be useful to show how you can create a simple test case. After creating andsaving our helper, we’ll create the test case file in app/Test/Case/View/Helper/ProgressHelperTest.php. Inthat file we’ll start with the following:
App::uses('Controller', 'Controller');
App::uses('View', 'View');
App::uses('ProgressHelper', 'View/Helper');class ProgressHelperTest extends CakeTestCase {public function setUp() {}public function testBar() {(continues on next page)

626

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}}We’ll flesh out this skeleton in a minute. We’ve added two methods to start with. First is setUp(). This method iscalled before every test method in a test case class. Setup methods should initialize the objects needed for the test, anddo any configuration needed. In our setup method we’ll add the following:
public function setUp() {parent::setUp();$Controller = new Controller();$View = new View($Controller);$this->Progress = new ProgressHelper($View);}
Calling the parent method is important in test cases, as CakeTestCase::setUp() does a number of things like backing up
the values in Configure and, storing the paths in App.
Next, we’ll fill out the test method. We’ll use some assertions to ensure that our code creates the output we expect:public function testBar() {$result = $this->Progress->bar(90);$this->assertContains('width: 90%', $result);$this->assertContains('progress-bar', $result);$result = $this->Progress->bar(33.3333333);$this->assertContains('width: 33%', $result);}
The above test is a simple one but shows the potential benefit of using test cases. We use assertContains() to ensurethat our helper is returning a string that contains the content we expect. If the result did not contain the expected content
the test would fail, and we would know that our code is incorrect.
By using test cases you can easily describe the relationship between a set of known inputs and their expected output.
This helps you be more confident of the code you’re writing as you can easily check that the code you wrote fulfills the
expectations and assertions your tests make. Additionally because tests are code, they are easy to re-run whenever you
make a change. This helps prevent the creation of new bugs.

Running Tests
Once you have PHPUnit installed and some test cases written, you’ll want to run the test cases very frequently. It’s agood idea to run tests before committing any changes to help ensure you haven’t broken anything.
Running tests from a browser
CakePHP provides a web interface for running tests, so you can execute your tests through a browser if you’re more
comfortable in that environment. You can access the web runner by going to http://localhost/your_app/test.
php. The exact location of test.php will change depending on your setup. But the file is at the same level as index.php.
Once you’ve loaded up the test runner, you can navigate App, Core and Plugin test suites. Clicking an individual test
case will run that test and display the results.

Testing627

CakePHP Cookbook Documentation, Release 2.x

Viewing code coverage
If you have Xdebug117 installed, you can view code coverage results. Code coverage is useful for telling you what partsof your code your tests do not reach. Coverage is useful for determining where you should add tests in the future, andgives you one measurement to track your testing progress with.

The inline code coverage uses green lines to indicate lines that have been run. If you hover over a green line a tooltip
will indicate which tests covered the line. Lines in red did not run, and have not been exercised by your tests. Greylines are considered unexecutable code by Xdebug.
Running tests from command line
CakePHP provides a test shell for running tests. You can run app, core and plugin tests easily using the test shell. It
accepts all the arguments you would expect to find on the normal PHPUnit command line tool as well. From your app
directory you can do the following to run tests:# Run a model tests in the app./Console/cake test app Model/Article# Run a component test in a plugin./Console/cake test DebugKit Controller/Component/ToolbarComponent# Run the configure class test in CakePHP./Console/cake test core Core/Configure
117 https://xdebug.org

628

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Note: If you are running tests that interact with the session it’s generally a good idea to use the --stderr option.
This will fix issues with tests failing because of headers_sent warnings.
Changed in version 2.1: The test shell was added in 2.1. The 2.0 testsuite shell is still available but the new syntax
is preferred.
You can also run test shell in the project root directory. This shows you a full list of all the tests that you currently
have. You can then freely choose what test(s) to run:# Run test in project root directory for application folder called app
lib/Cake/Console/cake test app# Run test in project root directory for an application in ./myapplib/Cake/Console/cake test --app myapp app

Filtering test cases
When you have larger test cases, you will often want to run a subset of the test methods when you are trying to workon a single failing case. With the CLI runner you can use an option to filter test methods:./Console/cake test core Console/ConsoleOutput --filter testWriteArray
The filter parameter is used as a case-sensitive regular expression for filtering which test methods to run.
Generating code coverage
You can generate code coverage reports from the command line using PHPUnit’s built-in code coverage tools. PHPUnit
will generate a set of static HTML files containing the coverage results. You can generate coverage for a test case by
doing the following:./Console/cake test app Model/Article --coverage-html webroot/coverage
This will put the coverage results in your application’s webroot directory. You should be able to view the results bygoing to http://localhost/your_app/coverage.
Running tests that use sessions
When running tests on the command line that use sessions you’ll need to include the --stderr flag. Failing to do sowill cause sessions to not work. PHPUnit outputs test progress to stdout by default, this causes PHP to assume thatheaders have been sent which prevents sessions from starting. By switching PHPUnit to output on stderr, this issue isavoided.

Testing629

CakePHP Cookbook Documentation, Release 2.x

Test Case Lifecycle Callbacks
Test cases have a number of lifecycle callbacks you can use when doing testing:• setUp is called before every test method. Should be used to create the objects that are going to be tested, andinitialize any data for the test. Always remember to call parent::setUp()• tearDown is called after every test method. Should be used to cleanup after the test is complete. Always remember to call parent::tearDown().
• setupBeforeClass is called once before test methods in a case are started. This method must be static.
• tearDownAfterClass is called once after test methods in a case are started. This method must be static.

Fixtures
When testing code that depends on models and the database, one can use fixtures as a way to generate temporary datatables loaded with sample data that can be used by the test. The benefit of using fixtures is that your test has no chanceof disrupting live application data. In addition, you can begin testing your code prior to actually developing live content
for an application.
CakePHP uses the connection named $test in your app/Config/database.php configuration file. If this connection
is not usable, an exception will be raised and you will not be able to use database fixtures.
CakePHP performs the following during the course of a fixture based test case:
1. Creates tables for each of the fixtures needed.
2. Populates tables with data, if data is provided in fixture.
3. Runs test methods.
4. Empties the fixture tables.
5. Removes fixture tables from database.
Creating fixtures
When creating a fixture you will mainly define two things: how the table is created (which fields are part of the table),and which records will be initially populated to the table. Let’s create our first fixture, that will be used to test our own
Article model. Create a file named ArticleFixture.php in your app/Test/Fixture directory, with the following
content:class ArticleFixture extends CakeTestFixture {// Optional.
// Set this property to load fixtures to a different test datasource
public $useDbConfig = 'test';
public $fields = array('id' => array('type' => 'integer', 'key' => 'primary'),'title' => array('type' => 'string','length' => 255,
'null' => false),'body' => 'text','published' => array((continues on next page)

630

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'type' => 'integer','default' => '0','null' => false),'created' => 'datetime','updated' => 'datetime');
public $records = array(
array('id' => 1,'title' => 'First Article','body' => 'First Article Body','published' => '1',
'created' => '2007-03-18 10:39:23','updated' => '2007-03-18 10:41:31'),
array('id' => 2,'title' => 'Second Article','body' => 'Second Article Body','published' => '1',
'created' => '2007-03-18 10:41:23','updated' => '2007-03-18 10:43:31'),
array('id' => 3,'title' => 'Third Article','body' => 'Third Article Body','published' => '1',
'created' => '2007-03-18 10:43:23','updated' => '2007-03-18 10:45:31'));}
The $useDbConfig property defines the datasource of which the fixture will use. If your application uses multiple
datasources, you should make the fixtures match the model’s datasources but prefixed with test_. For example if yourmodel uses the mydb datasource, your fixture should use the test_mydb datasource. If the test_mydb connection
doesn’t exist, your models will use the default test datasource. Fixture datasources must be prefixed with test to
reduce the possibility of accidentally truncating all your application’s data when running tests.
We use $fields to specify which fields will be part of this table, and how they are defined. The format used to define
these fields is the same used with CakeSchema. The keys available for table definition are:type
CakePHP internal data type. Currently supported:• string: maps to VARCHAR• text: maps to TEXT
• biginteger: maps to BIGINT• smallinteger: maps to SMALLINT• tinyinteger: maps to TINYINT or SMALLINT depending on the database platform.
Testing631

CakePHP Cookbook Documentation, Release 2.x• integer: maps to INT• float: maps to FLOAT• decimal: maps to DECIMAL• datetime: maps to DATETIME• timestamp: maps to TIMESTAMP• time: maps to TIME
• date: maps to DATE
• binary: maps to BLOB
• boolean: maps to BOOLEAN (or TINYINT(1) on MySQL).
key
Set to primary to make the field AUTO_INCREMENT, and a PRIMARY KEY for the table.
lengthSet to the specific length the field should take.
null
Set to either true (to allow NULLs) or false (to disallow NULLs).
default
Default value the field takes.
We can define a set of records that will be populated after the fixture table is created. The format is fairly straightforward, $records is an array of records. Each item in $records should be a single row. Inside each row, should be
an associative array of the columns and values for the row. Just keep in mind that each record in the $records arraymust have a key for every field specified in the $fields array. If a field for a particular record needs to have a nullvalue, just specify the value of that key as null.
Dynamic data and fixtures
Since records for a fixture are declared as a class property, you cannot easily use functions or other dynamic data todefine fixtures. To solve this problem, you can define $records in the init() function of your fixture. For example if
you wanted all the created and updated timestamps to reflect today’s date you could do the following:class ArticleFixture extends CakeTestFixture {public $fields = array('id' => array('type' => 'integer', 'key' => 'primary'),'title' => array('type' => 'string', 'length' => 255, 'null' => false),'body' => 'text','published' => array('type' => 'integer', 'default' => '0', 'null' => false),'created' => 'datetime','updated' => 'datetime');
public function init() {$this->records = array(
array('id' => 1,'title' => 'First Article','body' => 'First Article Body',(continues on next page)

632

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'published' => '1',
'created' => date('Y-m-d H:i:s'),'updated' => date('Y-m-d H:i:s'),),);parent::init();}}
When overriding init() just remember to always call parent::init().
Note: Please note that fixtures in 2.x do not handle foreign key constraints.

Importing table information and records
Your application may have already working models with real data associated to them, and you might decide to test yourapplication with that data. It would be then a duplicate effort to have to define the table definition and/or records on
your fixtures. Fortunately, there’s a way for you to define that table definition and/or records for a particular fixture
come from an existing model or an existing table.
Let’s start with an example. Assuming you have a model named Article available in your application (that maps to a table
named articles), change the example fixture given in the previous section (app/Test/Fixture/ArticleFixture.
php) to:class ArticleFixture extends CakeTestFixture {public $import = 'Article';}This statement tells the test suite to import your table definition from the table linked to the model called Article. You
can use any model available in your application. The statement will only import the Article schema, and does notimport records. To import records you can do the following:class ArticleFixture extends CakeTestFixture {public $import = array('model' => 'Article', 'records' => true);}
If on the other hand you have a table created but no model available for it, you can specify that your import will takeplace by reading that table information instead. For example:
class ArticleFixture extends CakeTestFixture {public $import = array('table' => 'articles');}Will import table definition from a table called ‘articles’ using your CakePHP database connection named ‘default’. If
you want to use a different connection use:class ArticleFixture extends CakeTestFixture {public $import = array('table' => 'articles', 'connection' => 'other');}
Since it uses your CakePHP database connection, if there’s any table prefix declared it will be automatically used when
fetching table information. The two snippets above do not import records from the table. To force the fixture to also
Testing633

CakePHP Cookbook Documentation, Release 2.x

import its records, change the import to:class ArticleFixture extends CakeTestFixture {public $import = array('table' => 'articles', 'records' => true);}
You can naturally import your table definition from an existing model/table, but have your records defined directly on
the fixture as it was shown on previous section. For example:
class ArticleFixture extends CakeTestFixture {public $import = 'Article';
public $records = array(
array('id' => 1,'title' => 'First Article','body' => 'First Article Body','published' => '1',
'created' => '2007-03-18 10:39:23','updated' => '2007-03-18 10:41:31'),
array('id' => 2,'title' => 'Second Article','body' => 'Second Article Body','published' => '1',
'created' => '2007-03-18 10:41:23','updated' => '2007-03-18 10:43:31'),
array('id' => 3,'title' => 'Third Article','body' => 'Third Article Body','published' => '1',
'created' => '2007-03-18 10:43:23','updated' => '2007-03-18 10:45:31'));}

Loading fixtures in your test cases
After you’ve created your fixtures, you’ll want to use them in your test cases. In each test case you should load the
fixtures you will need. You should load a fixture for every model that will have a query run against it. To load fixtures
you define the $fixtures property in your model:
class ArticleTest extends CakeTestCase {public $fixtures = array('app.article', 'app.comment');}
The above will load the Article and Comment fixtures from the application’s Fixture directory. You can also load
fixtures from CakePHP core, or plugins:

634

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

class ArticleTest extends CakeTestCase {public $fixtures = array('plugin.debug_kit.article', 'core.comment');}
Using the core prefix will load fixtures from CakePHP, and using a plugin name as the prefix, will load the fixture
from the named plugin.
You can control when your fixtures are loaded by setting CakeTestCase::$autoFixtures to false and later loadthem using CakeTestCase::loadFixtures():class ArticleTest extends CakeTestCase {public $fixtures = array('app.article', 'app.comment');
public $autoFixtures = false;
public function testMyFunction() {$this->loadFixtures('Article', 'Comment');}}
As of 2.5.0, you can load fixtures in subdirectories. Using multiple directories can make it easier to organize yourfixtures if you have a larger application. To load fixtures in subdirectories, simply include the subdirectory name in the
fixture name:class ArticleTest extends CakeTestCase {public $fixtures = array('app.blog/article', 'app.blog/comment');}
In the above example, both fixtures would be loaded from App/Test/Fixture/blog/.
Changed in version 2.5: As of 2.5.0 you can load fixtures in subdirectories.

Testing Models
Let’s say we already have our Article model defined on app/Model/Article.php, which looks like this:class Article extends AppModel {public function published($fields = null) {$params = array('conditions' => array($this->name . '.published' => 1),
'fields' => $fields);
return $this->find('all', $params);}}
We now want to set up a test that will use this model definition, but through fixtures, to test some functionality in themodel. CakePHP test suite loads a very minimum set of files (to keep tests isolated), so we have to start by loading ourmodel - in this case the Article model which we already defined.
Let’s now create a file named ArticleTest.php in your app/Test/Case/Model directory, with the following contents:

Testing635

CakePHP Cookbook Documentation, Release 2.x

App::uses('Article', 'Model');class ArticleTest extends CakeTestCase {public $fixtures = array('app.article');}In our test cases’ variable $fixtures we define the set of fixtures that we’ll use. You should remember to include all
the fixtures that will have queries run against them.
Note: You can override the test model database by specifying the $useDbConfig property. Ensure that the relevant
fixture uses the same value so that the table is created in the correct database.

Creating a test method
Let’s now add a method to test the function published() in the Article model. Edit the file app/Test/Case/Model/
ArticleTest.php so it now looks like this:
App::uses('Article', 'Model');class ArticleTest extends CakeTestCase {public $fixtures = array('app.article');public function setUp() {parent::setUp();$this->Article = ClassRegistry::init('Article');}public function testPublished() {$result = $this->Article->published(array('id', 'title'));$expected = array(
array('Article' => array('id' => 1, 'title' => 'First Article')),array('Article' => array('id' => 2, 'title' => 'Second Article')),array('Article' => array('id' => 3, 'title' => 'Third Article')));$this->assertEquals($expected, $result);}}
You can see we have added a method called testPublished(). We start by creating an instance of our Article
model, and then run our published() method. In $expected we set what we expect should be the proper result (thatwe know since we have defined which records are initially populated to the article table.) We test that the result equals
our expectation by using the assertEquals method. See the Running Tests section for more information on how to
run your test case.
Note: When setting up your Model for testing be sure to use ClassRegistry::init('YourModelName'); as it
knows to use your test database connection.

636

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Mocking model methods
There will be times you’ll want to mock methods on models when testing them. You should use getMockForModelto create testing mocks of models. It avoids issues with reflected properties that normal mocks have:public function testSendingEmails() {$model = $this->getMockForModel('EmailVerification', array('send'));$model->expects($this->once())->method('send')->will($this->returnValue(true));$model->verifyEmail('test@example.com');}
New in version 2.3: CakeTestCase::getMockForModel() was added in 2.3.

Testing Controllers
While you can test controller classes in a similar fashion to Helpers, Models, and Components, CakePHP offers a
specialized ControllerTestCase class. Using this class as the base class for your controller test cases allows you
to use testAction() for simpler test cases. ControllerTestCase allows you to easily mock out components andmodels, as well as potentially difficult to test methods like redirect().
Say you have a typical Articles controller, and its corresponding model. The controller code looks like:
App::uses('AppController', 'Controller');class ArticlesController extends AppController {public $helpers = array('Form', 'Html');public function index($short = null) {if (!empty($this->request->data)) {$this->Article->save($this->request->data);}
if (!empty($short)) {$result = $this->Article->find('all', array('id', 'title'));} else {$result = $this->Article->find('all');}if (isset($this->params['requested'])) {return $result;}$this->set('title', 'Articles');$this->set('articles', $result);}}
Create a file named ArticlesControllerTest.php in your app/Test/Case/Controller directory and put thefollowing inside:

Testing637

CakePHP Cookbook Documentation, Release 2.x

class ArticlesControllerTest extends ControllerTestCase {public $fixtures = array('app.article');public function testIndex() {$result = $this->testAction('/articles/index');debug($result);}
public function testIndexShort() {$result = $this->testAction('/articles/index/short');debug($result);}
public function testIndexShortGetRenderedHtml() {$result = $this->testAction('/articles/index/short',
array('return' => 'contents'));
debug($result);}
public function testIndexShortGetViewVars() {$result = $this->testAction('/articles/index/short',
array('return' => 'vars'));
debug($result);}
public function testIndexPostData() {$data = array('Article' => array('user_id' => 1,'published' => 1,'slug' => 'new-article','title' => 'New Article','body' => 'New Body'));$result = $this->testAction('/articles/index',array('data' => $data, 'method' => 'post'));
debug($result);}}
This example shows a few of the ways you can use testAction to test your controllers. The first parameter of testAction
should always be the URL you want to test. CakePHP will create a request and dispatch the controller and action.
When testing actions that contain redirect() and other code following the redirect it is generally a good idea to return
when redirecting. The reason for this, is that redirect() is mocked in testing, and does not exit like normal. And
instead of your code exiting, it will continue to run code following the redirect. For example:

638

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

App::uses('AppController', 'Controller');class ArticlesController extends AppController {public function add() {if ($this->request->is('post')) {if ($this->Article->save($this->request->data)) {$this->redirect(array('action' => 'index'));}}// more code}}
When testing the above code, you will still run // more code even when the redirect is reached. Instead, you should
write the code like:
App::uses('AppController', 'Controller');class ArticlesController extends AppController {public function add() {if ($this->request->is('post')) {if ($this->Article->save($this->request->data)) {return $this->redirect(array('action' => 'index'));}}// more code}}
In this case // more code will not be executed as the method will return once the redirect is reached.
Simulating GET requests
As seen in the testIndexPostData() example above, you can use testAction() to test POST actions as well as
GET actions. By supplying the data key, the request made to the controller will be POST. By default all requests will
be POST requests. You can simulate a GET request by setting the method key:
public function testAdding() {$data = array('Post' => array('title' => 'New post','body' => 'Secret sauce'));$this->testAction('/posts/add', array('data' => $data, 'method' => 'get'));// some assertions.
}
The data key will be used as query string parameters when simulating a GET request.

Testing639

CakePHP Cookbook Documentation, Release 2.x

Choosing the return type
You can choose from a number of ways to inspect the success of your controller action. Each offers a different way toensure your code is doing what you expect:• vars Get the set view variables.
• view Get the rendered view, without a layout.
• contents Get the rendered view including the layout.
• result Get the return value of the controller action. Useful for testing requestAction methods.
The default value is result. As long as your return type is not result you can also access the various other return
types as properties in the test case:public function testIndex() {$this->testAction('/posts/index');$this->assertInternalType('array', $this->vars['posts']);}Using mocks with testAction
There will be times when you want to replace components or models with either partially mocked objects or completely
mocked objects. You can do this by using ControllerTestCase::generate(). generate() takes the hard workout of generating mocks on your controller. If you decide to generate a controller to be used in testing, you can generatemocked versions of its models and components along with it:$Posts = $this->generate('Posts', array('methods' => array('isAuthorized'),'models' => array('Post' => array('save')),'components' => array('RequestHandler' => array('isPut'),'Email' => array('send'),'Session')));
The above would create a mocked PostsController, stubbing out the isAuthorized method. The attached Post
model will have save() stubbed, and the attached components would have their respective methods stubbed. You canchoose to stub an entire class by not passing methods to it, like Session in the example above.
Generated controllers are automatically used as the testing controller to test. To enable automatic generation, set the
autoMock variable on the test case to true. If autoMock is false, your original controller will be used in the test.
The response object in the generated controller is always replaced with a mock that does not send headers. After using
generate() or testAction() you can access the controller object at $this->controller.

640

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

A more complex example
In its simplest form, testAction() will run PostsController::index() on your testing controller (or an automatically generated one), including all of the mocked models and components. The results of the test are stored in thevars, contents, view, and return properties. Also available is a headers property which gives you access to theheaders that would have been sent, allowing you to check for redirects:
public function testAdd() {$Posts = $this->generate('Posts', array('components' => array('Session','Email' => array('send'))));$Posts->Session->expects($this->once())->method('setFlash');$Posts->Email->expects($this->once())->method('send')->will($this->returnValue(true));$this->testAction('/posts/add', array('data' => array('Post' => array('title' => 'New Post'))));$this->assertContains('/posts', $this->headers['Location']);}public function testAddGet() {$this->testAction('/posts/add', array('method' => 'GET','return' => 'contents'));$this->assertRegExp('/<html/', $this->contents);$this->assertRegExp('/<form/', $this->view);}
This example shows a slightly more complex use of the testAction() and generate() methods. First, we generatea testing controller and mock the SessionComponent. Now that the SessionComponent is mocked, we have the ability
to run testing methods on it. Assuming PostsController::add() redirects us to index, sends an email and sets aflash message, the test will pass. A second test was added to do basic sanity testing when fetching the add form. We
check to see if the layout was loaded by checking the entire rendered contents, and checks the view for a form tag. As
you can see, your freedom to test controllers and easily mock its classes is greatly expanded with these changes.
When doing controller tests using mocks that use static methods you’ll have to use a different method to register yourmock expectations. For example if you wanted to mock out AuthComponent::user() you’d have to do the following:
public function testAdd() {$Posts = $this->generate('Posts', array('components' => array('Session','Auth' => array('user')(continues on next page)

Testing641

CakePHP Cookbook Documentation, Release 2.x(continued from previous page))));$Posts->Auth->staticExpects($this->any())->method('user')->with('id')->will($this->returnValue(2));}
By using staticExpects you will be able to mock and manipulate static methods on components and models.
Warning: If you are using PHPUnit 4 or 5, staticExpects() does not exist anymore. Instead, you should insert
the necessary data into the session with CakeSession::write('Auth.User', $user) before calling the action.

Testing a JSON Responding Controller
JSON is a very friendly and common format to use when building a web service. Testing the endpoints of your web
service is very simple with CakePHP. Let us begin with a simple example controller that responds in JSON:
class MarkersController extends AppController {public $autoRender = false;
public function index() {$data = $this->Marker->find('first');$this->response->body(json_encode($data));}}
Now we create the file app/Test/Case/Controller/MarkersControllerTest.php and make sure our web service is returning the proper response:class MarkersControllerTest extends ControllerTestCase {public function testIndex() {$result = $this->testAction('/markers/index.json');$result = json_decode($result, true);$expected = array('Marker' => array('id' => 1, 'lng' => 66, 'lat' => 45),);
$this->assertEquals($expected, $result);}}

Testing Views
Generally most applications will not directly test their HTML code. Doing so is often results in fragile, difficult tomaintain test suites that are prone to breaking. When writing functional tests using ControllerTestCase you caninspect the rendered view content by setting the return option to ‘view’. While it is possible to test view content
using ControllerTestCase, more robust and maintainable integration/view testing can be accomplished using tools like
Selenium webdriver118 .
118 https://www.selenium.dev/

642

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Testing Components
Let’s pretend we have a component called PagematronComponent in our application. This component helps us set
the pagination limit value across all the controllers that use it. Here is our example component located in app/
Controller/Component/PagematronComponent.php:
class PagematronComponent extends Component {public $Controller = null;public function startup(Controller $controller) {parent::startup($controller);$this->Controller = $controller;// Make sure the controller is using pagination
if (!isset($this->Controller->paginate)) {$this->Controller->paginate = array();}}
public function adjust($length = 'short') {switch ($length) {case 'long':$this->Controller->paginate['limit'] = 100;
break;
case 'medium':$this->Controller->paginate['limit'] = 50;
break;
default:$this->Controller->paginate['limit'] = 20;
break;}}}
Now we can write tests to ensure our paginate limit parameter is being set correctly by the adjust method in ourcomponent. We create the file app/Test/Case/Controller/Component/PagematronComponentTest.php:
App::uses('Controller', 'Controller');
App::uses('CakeRequest', 'Network');
App::uses('CakeResponse', 'Network');
App::uses('ComponentCollection', 'Controller');
App::uses('PagematronComponent', 'Controller/Component');// A fake controller to test against
class PagematronControllerTest extends Controller {public $paginate = null;}class PagematronComponentTest extends CakeTestCase {public $PagematronComponent = null;public $Controller = null;public function setUp() {parent::setUp();// Setup our component and fake test controller

Testing(continues on next page)643

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$Collection = new ComponentCollection();$this->PagematronComponent = new PagematronComponent($Collection);$CakeRequest = new CakeRequest();$CakeResponse = new CakeResponse();$this->Controller = new PagematronControllerTest($CakeRequest, $CakeResponse);$this->PagematronComponent->startup($this->Controller);}public function testAdjust() {// Test our adjust method with different parameter settings$this->PagematronComponent->adjust();$this->assertEquals(20, $this->Controller->paginate['limit']);$this->PagematronComponent->adjust('medium');$this->assertEquals(50, $this->Controller->paginate['limit']);$this->PagematronComponent->adjust('long');$this->assertEquals(100, $this->Controller->paginate['limit']);}public function tearDown() {parent::tearDown();// Clean up after we're done
unset($this->PagematronComponent);
unset($this->Controller);}}

Testing Helpers
Since a decent amount of logic resides in Helper classes, it’s important to make sure those classes are covered by test
cases.
First we create an example helper to test. The CurrencyRendererHelper will help us display currencies in our views
and for simplicity only has one method usd().
// app/View/Helper/CurrencyRendererHelper.php
class CurrencyRendererHelper extends AppHelper {public function usd($amount) {return 'USD ' . number_format($amount, 2, '.', ',');}}
Here we set the decimal places to 2, decimal separator to dot, thousands separator to comma, and prefix the formatted
number with ‘USD’ string.
Now we create our tests:// app/Test/Case/View/Helper/CurrencyRendererHelperTest.php
App::uses('Controller', 'Controller');
App::uses('View', 'View');(continues on next page)

644

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

App::uses('CurrencyRendererHelper', 'View/Helper');class CurrencyRendererHelperTest extends CakeTestCase {public $CurrencyRenderer = null;// Here we instantiate our helper
public function setUp() {parent::setUp();$Controller = new Controller();$View = new View($Controller);$this->CurrencyRenderer = new CurrencyRendererHelper($View);}// Testing the usd() function
public function testUsd() {$this->assertEquals('USD 5.30', $this->CurrencyRenderer->usd(5.30));// We should always have 2 decimal digits$this->assertEquals('USD 1.00', $this->CurrencyRenderer->usd(1));$this->assertEquals('USD 2.05', $this->CurrencyRenderer->usd(2.05));// Testing the thousands separator$this->assertEquals('USD 12,000.70',$this->CurrencyRenderer->usd(12000.70));}}
Here, we call usd() with different parameters and tell the test suite to check if the returned values are equal to what is
expected.
Save this in and execute the test. You should see a green bar and messaging indicating 1 pass and 4 assertions.

Creating Test Suites
If you want several of your tests to run at the same time, you can create a test suite. A test suite is composed of several
test cases. CakeTestSuite offers a few methods for easily creating test suites based on the file system. If we wanted
to create a test suite for all our model tests we would create app/Test/Case/AllModelTest.php. Put the following
in it:
class AllModelTest extends CakeTestSuite {public static function suite() {$suite = new CakeTestSuite('All model tests');$suite->addTestDirectory(TESTS . 'Case/Model');return $suite;}}
The code above will group all test cases found in the /app/Test/Case/Model/ folder. To add an individual file, use$suite->addTestFile($filename);. You can recursively add a directory for all tests using:

Testing645

CakePHP Cookbook Documentation, Release 2.x$suite->addTestDirectoryRecursive(TESTS . 'Case/Model');Would recursively add all test cases in the app/Test/Case/Model directory. You can use test suites to build a suitethat runs all your application’s tests:
class AllTestsTest extends CakeTestSuite {public static function suite() {$suite = new CakeTestSuite('All tests');$suite->addTestDirectoryRecursive(TESTS . 'Case');return $suite;}}
You can then run this test on the command line using:$ Console/cake test app AllTests

Creating Tests for Plugins
Tests for plugins are created in their own directory inside the plugins folder.
/app/Plugin
/Blog
/Test
/Case
/Fixture
They work just like normal tests but you have to remember to use the naming conventions for plugins when importingclasses. This is an example of a testcase for the BlogPost model from the plugins chapter of this manual. A difference
from other tests is in the first line where ‘Blog.BlogPost’ is imported. You also need to prefix your plugin fixtures withplugin.blog.blog_post:
App::uses('BlogPost', 'Blog.Model');class BlogPostTest extends CakeTestCase {// Plugin fixtures located in /app/Plugin/Blog/Test/Fixture/public $fixtures = array('plugin.blog.blog_post');public $BlogPost;
public function testSomething() {// ClassRegistry makes the model use the test database connection$this->BlogPost = ClassRegistry::init('Blog.BlogPost');// do some useful test here
$this->assertTrue(is_object($this->BlogPost));}}If you want to use plugin fixtures in the app tests you can reference them using plugin.pluginName.fixtureName
syntax in the $fixtures array.

646

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Integration with Jenkins
Jenkins119 is a continuous integration server, that can help you automate the running of your test cases. This helps
ensure that all your tests stay passing and your application is always ready.
Integrating a CakePHP application with Jenkins is fairly straightforward. The following assumes you’ve already installed Jenkins on *nix system, and are able to administer it. You also know how to create jobs, and run builds. If you
are unsure of any of these, refer to the Jenkins documentation120 .
Create a job
Start off by creating a job for your application, and connect your repository so that jenkins can access your code.
Add test database config
Using a separate database just for Jenkins is generally a good idea, as it stops bleed through and avoids a number ofbasic problems. Once you’ve created a new database in a database server that jenkins can access (usually localhost).
Add a shell script step to the build that contains the following:
cat > app/Config/database.php <<'DATABASE_PHP'<?php
class DATABASE_CONFIG {public $test = array('datasource' => 'Database/Mysql','host'=> 'localhost','database'=> 'jenkins_test','login'=> 'jenkins','password'=> 'cakephp_jenkins','encoding'=> 'utf8');}
DATABASE_PHP
This ensures that you’ll always have the correct database configuration that Jenkins requires. Do the same for any otherconfiguration files you need to. It’s often a good idea to drop and re-create the database before each build as well. This
insulates you from chained failures, where one broken build causes others to fail. Add another shell script step to thebuild that contains the following:
mysql -u jenkins -pcakephp_jenkins -e 'DROP DATABASE IF EXISTS jenkins_test; CREATE␣˓→DATABASE jenkins_test';
119 https://jenkins-ci.org

120 https://jenkins-ci.org/

Testing647

CakePHP Cookbook Documentation, Release 2.x

Add your tests
Add another shell script step to your build. In this step run the tests for your application. Creating a junit log file, orclover coverage is often a nice bonus, as it gives you a nice graphical view of your testing results:
app/Console/cake test app AllTests \--stderr \--log-junit junit.xml \--coverage-clover clover.xml
If you use clover coverage, or the junit results, make sure to configure those in Jenkins as well. Failing to configure
those steps will mean you won’t see the results.
Run a build
You should be able to run a build now. Check the console output and make any necessary changes to get a passingbuild.

REST
Many newer application programmers are realizing the need to open their core functionality to a greater audience.
Providing easy, unfettered access to your core API can help get your platform accepted, and allows for mashups andeasy integration with other systems.
While other solutions exist, REST is a great way to provide easy access to the logic you’ve created in your application.
It’s simple, usually XML-based (we’re talking simple XML, nothing like a SOAP envelope), and depends on HTTP
headers for direction. Exposing an API via REST in CakePHP is simple.

The Simple Setup
The fastest way to get up and running with REST is to add a few lines to your routes.php file, found in app/Config.
The Router object features a method called mapResources(), that is used to set up a number of default routes for
REST access to your controllers. Make sure mapResources() comes before require CAKE . 'Config' . DS .
'routes.php'; and other routes which would override the routes. If we wanted to allow REST access to a recipe
database, we’d do something like this://In app/Config/routes.php...
Router::mapResources('recipes');Router::parseExtensions();
The first line sets up a number of default routes for easy REST access while parseExtensions() method specifies
the desired result format (e.g. xml, json, rss). These routes are HTTP Request Method sensitive.

648

HTTP format

URL format

Controller action invoked

GET
GET
POST
POST
PUT
DELETE/recipes.format/recipes/123.format/recipes.format/recipes/123.format/recipes/123.format/recipes/123.format

RecipesController::index()
RecipesController::view(123)RecipesController::add()
RecipesController::edit(123)RecipesController::edit(123)RecipesController::delete(123)
Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

CakePHP’s Router class uses a number of different indicators to detect the HTTP method being used. Here they are inorder of preference:
1. The _method POST variable
2. The X_HTTP_METHOD_OVERRIDE
3. The REQUEST_METHOD header
The _method POST variable is helpful in using a browser as a REST client (or anything else that can do POST easily).
Just set the value of _method to the name of the HTTP request method you wish to emulate.
Once the router has been set up to map REST requests to certain controller actions, we can move on to creating the
logic in our controller actions. A basic controller might look something like this:// Controller/RecipesController.php
class RecipesController extends AppController {public $components = array('RequestHandler');
public function index() {$recipes = $this->Recipe->find('all');$this->set(array('recipes' => $recipes,'_serialize' => array('recipes')));}
public function view($id) {$recipe = $this->Recipe->findById($id);$this->set(array('recipe' => $recipe,'_serialize' => array('recipe')));}
public function add() {$this->Recipe->create();if ($this->Recipe->save($this->request->data)) {$message = 'Saved';} else {$message = 'Error';}$this->set(array('message' => $message,'_serialize' => array('message')));}
public function edit($id) {$this->Recipe->id = $id;
if ($this->Recipe->save($this->request->data)) {$message = 'Saved';} else {$message = 'Error';}(continues on next page)

REST

649

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$this->set(array('message' => $message,'_serialize' => array('message')));}
public function delete($id) {if ($this->Recipe->delete($id)) {$message = 'Deleted';} else {$message = 'Error';}$this->set(array('message' => $message,'_serialize' => array('message')));}}
Since we’ve added a call to Router::parseExtensions(), the CakePHP router is already primed to serve up different
views based on different kinds of requests. Since we’re dealing with REST requests, we’ll be making XML views. You
can also easily make JSON views using CakePHP’s built-in JSON and XML views. By using the built in XmlView we
can define a _serialize view variable. This special view variable is used to define which view variables XmlView
should serialize into XML.
If we wanted to modify the data before it is converted into XML we should not define the _serialize view variable,
and instead use view files. We place the REST views for our RecipesController inside app/View/recipes/xml. We
can also use the Xml for quick-and-easy XML output in those views. Here’s what our index view might look like:// app/View/Recipes/xml/index.ctp// Do some formatting and manipulation on// the $recipes array.
$xml = Xml::fromArray(array('response' => $recipes));echo $xml->asXML();
When serving up a specific content type using parseExtensions(), CakePHP automatically looks for a view helper thatmatches the type. Since we’re using XML as the content type, there is no built-in helper, however if you were to create
one it would automatically be loaded for our use in those views.
The rendered XML will end up looking something like this:<recipes><recipe id="234" created="2008-06-13" modified="2008-06-14"><author id="23423" first_name="Billy" last_name="Bob"></author><comment id="245" body="Yummy yummmy"></comment></recipe><recipe id="3247" created="2008-06-15" modified="2008-06-15"><author id="625" first_name="Nate" last_name="Johnson"></author><comment id="654" body="This is a comment for this tasty dish."></comment></recipe></recipes>
Creating the logic for the edit action is a bit trickier, but not by much. Since you’re providing an API that outputs XML,
it’s a natural choice to receive XML as input. Not to worry, the RequestHandler and Router classes make things

650

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

much easier. If a POST or PUT request has an XML content-type, then the input is run through CakePHP’s Xml class,and the array representation of the data is assigned to $this->request->data. Because of this feature, handling XML
and POST data in parallel is seamless: no changes are required to the controller or model code. Everything you needshould end up in $this->request->data.

Accepting Input in Other Formats
Typically REST applications not only output content in alternate data formats, but also accept data in different formats. In CakePHP, the RequestHandlerComponent helps facilitate this. By default, it will decode
any incoming JSON/XML input data for POST/PUT requests and supply the array version of that data in $this>request->data. You can also wire in additional deserializers for alternate formats if you need them, using
RequestHandler::addInputType().

Modifying the default REST routes
New in version 2.1.
If the default REST routes don’t work for your application, you can modify them using Router::resourceMap().
This method allows you to set the default routes that get set with Router::mapResources(). When using this method
you need to set all the defaults you want to use:Router::resourceMap(array(
array('action' => 'index', 'method' => 'GET', 'id' => false),
array('action' => 'view', 'method' => 'GET', 'id' => true),
array('action' => 'add', 'method' => 'POST', 'id' => false),
array('action' => 'edit', 'method' => 'PUT', 'id' => true),
array('action' => 'delete', 'method' => 'DELETE', 'id' => true),
array('action' => 'update', 'method' => 'POST', 'id' => true)));
By overwriting the default resource map, future calls to mapResources() will use the new values.

Custom REST Routing
If the default routes created by Router::mapResources() don’t work for you, use the Router::connect() method
to define a custom set of REST routes. The connect() method allows you to define a number of different options fora given URL. See the section on Using Additional Conditions When Matching Routes for more information.
New in version 2.5.
You can provide connectOptions key in the $options array for Router::mapResources() to provide custom
setting used by Router::connect():
Router::mapResources('books', array('connectOptions' => array('routeClass' => 'ApiRoute',
)
));

REST

651

CakePHP Cookbook Documentation, Release 2.x

Dispatcher Filters
New in version 2.2.
There are several reasons to want a piece of code to be run before any controller code is executed or right before theresponse is sent to the client, such as response caching, header tuning, special authentication or just to provide accessto a mission-critical API response in lesser time than a complete request dispatching cycle would take.
CakePHP provides for such cases a clean and extensible interface for attaching filters to this dispatching cycle, similar
to a middleware layer thought to provide stackable services or routines for every request. We call them Dispatcher
Filters

Configuring Filters
Filters are usually configured in the bootstrap.php file, but you could easily load them from any other configuration
file before the request is dispatched. Adding and removing filters is done through the Configure class, using the special
key Dispatcher.filters. By default CakePHP comes with a couple filter classes already enabled for all requests,
let’s take a look at how they are added:
Configure::write('Dispatcher.filters', array('AssetDispatcher','CacheDispatcher'));
Each of those array values are class names that will be instantiated and added as listeners for the events generated at
dispatcher level. The first one, AssetDispatcher is meant to check whether the request is referring to a theme or plugin
asset file, such as a CSS, JavaScript or image stored on either a plugin’s webroot folder or the corresponding one for a
Theme. It will serve the file accordingly if found, stopping the rest of the dispatching cycle. The CacheDispatcher
filter, when Cache.check config variable is enabled, will check if the response was already cached in the file system
for a similar request and serve the cached code immediately.
As you can see, both provided filters have the responsibility of stopping any further code and send the response rightaway to the client. But filters are not limited to this role, as we will show shortly in this section.
You can add your own class names to the list of filters, and they will get executed in the order they were defined. Thereis also an alternative way for attaching filters that do not involve the special DispatcherFilter classes:
Configure::write('Dispatcher.filters', array('my-filter' => array('callable' => array($classInstance, 'methodName'),'on' => 'after')));
As shown above, you can pass any valid PHP callback121 type, as you may remember, a callback is anything that PHP
can execute with call_user_func. We do make a little exception, if a string is provided it will be treated as a class
name, not as a possible function name. This of course gives the ability to PHP 5.3 users to attach anonymous functions
as filters:
Configure::write('Dispatcher.filters', array('my-filter' => array('callable' => function($event) {...}, 'on' => 'before'),//more filters here));
121 https://www.php.net/callback

652

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

The on key only takes before and after as valid values, and evidently means whether the filter should run before orafter any controller code is executed. Additionally to defining filters with the callable key, you also get the chance
to define a priority for your filters, if none is specified then a default of 10 is selected for you
As all filters will have default priority 10, should you want to run a filter before any other in the list, select lower priority
numbers as needed:
Configure::write('Dispatcher.filters', array('my-filter' => array('callable' => function($event) {...},'on' => 'before','priority' => 5),'other-filter' => array('callable' => array($class, 'method'),'on' => 'after','priority' => 1),//more filters here));
Obviously, when defining priorities the order in which filters are declared does not matter but for those having the same.
When defining filters as class names there is no option to define priority in-line, we will get into that soon. Finally,
CakePHP’s plugin notation can be used to define filters located in plugins:
Configure::write('Dispatcher.filters', array('MyPlugin.MyFilter',));Feel free to remove the default attached filters if you choose to use a more advanced/faster way of serving theme andplugin assets or if you do not wish to use built-in full page caching, or just implement your own.
If you need to pass constructor parameters or settings to you dispatch filter classes you can do that by providing anarray of settings:
Configure::write('Dispatcher.filters', array('MyAssetFilter' => array('service' => 'google.com')));
When the filter key is a valid classname, the value can be an array of parameters that are passed to the dispatch filter.
By default the base class will assign these settings to the $settings property after merging them with the defaults inthe class.
Changed in version 2.5: You can now provide constructor settings to dispatch filters in 2.5.

Filter Classes
Dispatcher filters, when defined as class names in configuration, should extend the class DispatcherFilter provided
in the Routing CakePHP’s directory. Let’s create a simple filter to respond to a specific URL with a ‘Hello World’ text:
App::uses('DispatcherFilter', 'Routing');class HelloWorldFilter extends DispatcherFilter {public $priority = 9;(continues on next page)

Dispatcher Filters

653

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

public function beforeDispatch(CakeEvent $event) {$request = $event->data['request'];$response = $event->data['response'];if ($request->url === 'hello-world') {$response->body('Hello World');$event->stopPropagation();return $response;}}}This class should be saved in a file in app/Routing/Filter/HelloWorldFilter.php and configured in the bootstrap file according to how it was explained in the previous section. There is plenty to explain here, let’s begin with the$priority value.
As mentioned before, when using filter classes you can only define the order in which they are run using the $priority
property in the class, default value is 10 if the property is declared, this means that it will get executed _after_ the Routerclass has parsed the request. We do not want this to happen in our previous example, because most probably you do
not have any controller set up for answering to that URL, hence we chose 9 as our priority.
DispatcherFilter exposes two methods that can be overridden in subclasses, they are beforeDispatch andafterDispatch, and are executed before or after any controller is executed respectively. Both methods receive a
CakeEvent object containing the request and response objects (CakeRequest and CakeResponse instances) alongwith an additionalParams array inside the data property. The latter contains information used for internal dispatching when calling requestAction.
In our example we conditionally returned the $response object as a result, this will tell the Dispatcher to
not instantiate any controller and return such object as response immediately to the client. We also added$event->stopPropagation() to prevent other filters from being executed after this one.
Let’s now create another filter for altering response headers in any public page, in our case it would be anything served
from the PagesController:
App::uses('DispatcherFilter', 'Routing');class HttpCacheFilter extends DispatcherFilter {public function afterDispatch(CakeEvent $event) {$request = $event->data['request'];$response = $event->data['response'];if ($request->params['controller'] !== 'pages') {return;}if ($response->statusCode() === 200) {$response->sharable(true);$response->expires(strtotime('+1 day'));}}}This filter will send a expiration header to 1 day in the future for all responses produced by the pages controller.
You could of course do the same in the controller, this is just an example of what could be done with filters. For
instance, instead of altering the response you could cache it using the Cache class and serve the response from thebeforeDispatch callback.
654

Chapter 10. Development

CakePHP Cookbook Documentation, Release 2.x

Inline Filters
Our last example will use an anonymous function (only available on PHP 5.3+) to serve a list of posts in JSON format, we
encourage you to do so using controllers and the JsonView class, but let’s imagine you need to save a few milliseconds
for this mission-critical API endpoint:$postsList = function($event) {if ($event->data['request']->url !== 'posts/recent.json') {return;}App::uses('ClassRegistry', 'Utility');$postModel = ClassRegistry::init('Post');$event->data['response']->body(json_encode($postModel->find('recent')));$event->stopPropagation();return $event->data['response'];};Configure::write('Dispatcher.filters', array('AssetDispatcher','CacheDispatcher','recent-posts' => array('callable' => $postsList,'priority' => 9,'on'=> 'before')));
In previous example we have selected a priority of 9 for our filter, so to skip any other logic either placed in custom orcore filters such as CakePHP internal routing system. Although it is not required, it shows how to make your importantcode run first in case you need to trim as much fat as possible from some requests.
For obvious reasons this has the potential of making your app very difficult to maintain. Filters are an extremely
powerful tool when used wisely, adding response handlers for each URL in your app is not a good use for it. But if you
got a valid reason to do so, then you have a clean solution at hand. Keep in mind that not everything needs to be a filter,
Controllers and Components are usually a more accurate choice for adding any request handling code to your app.

Dispatcher Filters

655

CakePHP Cookbook Documentation, Release 2.x

656

Chapter 10. Development

CHAPTER 11

Deployment

Once your application is complete, or even before that you’ll want to deploy it. There are a few things you should dowhen deploying a CakePHP application.

Check your security
If you’re throwing your application out into the wild, it’s a good idea to make sure it doesn’t have any leaks. Check
the Security to guard against CSRF attacks, form field tampering, and others. Doing Data Validation, and/or Data
Sanitization is also a great idea, for protecting your database and also against XSS attacks. Check that only yourwebroot directory is publicly visible, and that your secrets (such as your app salt and any security keys) are private
and unique as well!

Set document root
Setting the document root correctly on your application is an important step to keeping your code secure and yourapplication safer. CakePHP applications should have the document root set to the application’s app/webroot. This
makes the application and configuration files inaccessible through a URL. Setting the document root is different fordifferent webservers. See the URL Rewriting documentation for webserver specific information.
In all cases you will want to set the virtual host/domain’s document to be app/webroot/. This removes the possibilityof files outside of the webroot directory being executed.

657

CakePHP Cookbook Documentation, Release 2.x

Update core.php
Updating core.php, specifically the value of debug is extremely important. Turning debug = 0 disables a number ofdevelopment features that should never be exposed to the Internet at large. Disabling debug changes the following typesof things:• Debug messages, created with pr() and debug() are disabled.
• Core CakePHP caches are by default flushed every 999 days, instead of every 10 seconds as in development.
• Error views are less informative, and give generic error messages instead.
• Errors are not displayed.
• Exception stack traces are disabled.
In addition to the above, many plugins and application extensions use debug to modify their behavior.
You can check against an environment variable to set the debug level dynamically between environments. This will
avoid deploying an application with debug > 0 and also save yourself from having to change the debug level each time
before deploying to a production environment.
For example, you can set an environment variable in your Apache configuration:
SetEnv CAKEPHP_DEBUG 2
And then you can set the debug level dynamically in core.php:
if (getenv('CAKEPHP_DEBUG')) {Configure::write('debug', 2);} else {Configure::write('debug', 0);}

Improve your application’s performance
Since handling static assets, such as images, JavaScript and CSS files of plugins, through the Dispatcher is incredibly
inefficient, it is strongly recommended to symlink them for production. For example like this:ln -s app/Plugin/YourPlugin/webroot/css/yourplugin.css app/webroot/css/yourplugin.css

658

Chapter 11. Deployment

CHAPTER 12

Tutorials & Examples

In this section, you can walk through typical CakePHP applications to see how all of the pieces come together.
Alternatively, you can refer to the non-official CakePHP plugin repository CakePackages122 and the Bakery123 forexisting applications and components.

Blog Tutorial
Welcome to CakePHP. You’re probably checking out this tutorial because you want to learn more about how CakePHP
works. It’s our aim to increase productivity and make coding more enjoyable: we hope you’ll see this as you dive into
the code.
This tutorial will walk you through the creation of a simple blog application. We’ll be getting and installing CakePHP,
creating and configuring a database, and creating enough application logic to list, add, edit, and delete blog posts.
Here’s what you’ll need:
1. A running web server. We’re going to assume you’re using Apache, though the instructions for using otherservers should be very similar. We might have to play a little with the server configuration, but most folks can
get CakePHP up and running without any configuration at all. Make sure you have PHP 5.2.8 or greater.
2. A database server. We’re going to be using MySQL server in this tutorial. You’ll need to know enough about
SQL in order to create a database: CakePHP will be taking the reins from there. Since we’re using MySQL, also
make sure that you have pdo_mysql enabled in PHP.
3. Basic PHP knowledge. The more object-oriented programming you’ve done, the better: but fear not if you’re aprocedural fan.
4. Finally, you’ll need a basic knowledge of the MVC programming pattern. A quick overview can be found in
Understanding Model-View-Controller. Don’t worry, it’s only half a page or so.
122 https://plugins.cakephp.org/
123 https://bakery.cakephp.org/

659

CakePHP Cookbook Documentation, Release 2.x

Let’s get started!

Getting CakePHPFirst, let’s get a copy of fresh CakePHP code.
To get a fresh download, visit the CakePHP project on GitHub: https://github.com/cakephp/cakephp/tags and download
the latest release of 2.0
You can also clone the repository using git124 :git clone -b 2.x git://github.com/cakephp/cakephp.git
Regardless of how you downloaded it, place the code inside of your DocumentRoot. Once finished, your directory
setup should look something like the following:/path_to_document_root
/app
/lib
/plugins/vendors.htaccessindex.php
READMENow might be a good time to learn a bit about how CakePHP’s directory structure works: check out the CakePHP
Folder Structure section.
Tmp directory permissions
Next we’ll need to make the app/tmp directory writable by the webserver. The best way to do this is to find out whatuser your webserver runs as. You can run <?php echo exec('whoami'); ?> inside any PHP file your webserver
can execute. You should see a username printed. Change the ownership of the app/tmp directory to that user. The
final command you run (in *nix) might look something like this:$ chown -R www-data app/tmp
If for some reason CakePHP can’t write to that directory, you’ll see warnings and uncaught exceptions that cache datacannot be written.

Creating the Blog Database
Next, let’s set up the underlying database for our blog. If you haven’t already done so, create an empty database for use
in this tutorial, with a name of your choice. Right now, we’ll just create a single table to store our posts. We’ll also
throw in a few posts right now to use for testing purposes. Execute the following SQL statements into your database:/* First, create our posts table: */CREATE TABLE posts (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(50),body TEXT,(continues on next page)
124 https://git-scm.com/

660

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL);/* Then insert some posts for testing: */INSERT INTO posts (title, body, created)
VALUES ('The title', 'This is the post body.', NOW());
INSERT INTO posts (title, body, created)
VALUES ('A title once again', 'And the post body follows.', NOW());
INSERT INTO posts (title, body, created)
VALUES ('Title strikes back', 'This is really exciting! Not.', NOW());
The choices on table and column names are not arbitrary. If you follow CakePHP’s database naming conventions, and
CakePHP’s class naming conventions (both outlined in CakePHP Conventions), you’ll be able to take advantage of alot of free functionality and avoid configuration. CakePHP is flexible enough to accommodate even the worst legacy
database schema, but adhering to convention will save you time.
Check out CakePHP Conventions for more information, but suffice it to say that naming our table ‘posts’ automatically hooks it to our Post model, and having fields called ‘modified’ and ‘created’ will be automagically managed by
CakePHP.

CakePHP Database Configuration
Onward and upward: let’s tell CakePHP where our database is and how to connect to it. For many, this is the first andlast time you configure anything.
A copy of CakePHP’s database configuration file is found in /app/Config/database.php.default. Make a copyof this file in the same directory, but name it database.php.
The config file should be pretty straightforward: just replace the values in the $default array with those that apply toyour setup. A sample completed configuration array might look something like the following:
public $default = array('datasource' => 'Database/Mysql','persistent' => false,'host' => 'localhost','port' => '','login' => 'cakeBlog','password' => 'c4k3-rUl3Z','database' => 'cake_blog_tutorial','schema' => '','prefix' => '','encoding' => 'utf8');
Once you’ve saved your new database.php file, you should be able to open your browser and see the CakePHP
welcome page. It should also tell you that your database connection file was found, and that CakePHP can successfully
connect to the database.
Note: Remember that you’ll need to have PDO, and pdo_mysql enabled in your php.ini.

Blog Tutorial

661

CakePHP Cookbook Documentation, Release 2.x

Optional Configuration
There are a few other items that can be configured. Most developers complete these laundry-list items, but they’re notrequired for this tutorial. One is defining a custom string (or “salt”) for use in security hashes. The second is defining
a custom number (or “seed”) for use in encryption.
The security salt is used for generating hashes. Change the default Security.salt value in /app/Config/core.php.
The replacement value should be long, hard to guess and be as random as you can make it:/*** A random string used in security hashing methods.
*/
Configure::write('Security.salt', 'pl345e-P45s_7h3*S@l7!');The cipher seed is used for encrypt/decrypt strings. Change the default Security.cipherSeed value by editing/app/Config/core.php. The replacement value should be a large random integer:/*** A random numeric string (digits only) used to encrypt/decrypt strings.
*/
Configure::write('Security.cipherSeed', '7485712659625147843639846751');

A Note on mod_rewrite
Occasionally new users will run into mod_rewrite issues. For example if the CakePHP welcome page looks a little
funny (no images or CSS styles), it probably means mod_rewrite is not functioning on your system. Please refer to oneof the sections below about URL rewriting for your webserver to get you up and running:
Now continue to Blog Tutorial - Adding a layer to start building your first CakePHP application.

Blog Tutorial - Adding a layer
Create a Post Model
The Model class is the bread and butter of CakePHP applications. By creating a CakePHP model that will interact with
our database, we’ll have the foundation in place needed to do our view, add, edit, and delete operations later.
CakePHP’s model class files go in /app/Model, and the file we’ll be creating will be saved to /app/Model/Post.php.
The completed file should look like this:class Post extends AppModel {}Naming conventions are very important in CakePHP. By naming our model Post, CakePHP can automatically infer that
this model will be used in the PostsController, and will be tied to a database table called posts.
Note: CakePHP will dynamically create a model object for you if it cannot find a corresponding file in /app/Model.
This also means that if you accidentally name your file wrong (for example, post.php or posts.php instead of Post.php),
CakePHP will not recognize any of your settings and will use the defaults instead.
For more on models, such as table prefixes, callbacks, and validation, check out the Models chapter of the Manual.

662

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x

Create a Posts Controller
Next, we’ll create a controller for our posts. The controller is where all the controlling logic for post interaction will
happen. In a nutshell, it’s the place where you play with the models and get post-related work done. We’ll place thisnew controller in a file called PostsController.php inside the /app/Controller directory. Here’s what the basic
controller should look like:class PostsController extends AppController {public $helpers = array('Html', 'Form');}
Now, let’s add an action to our controller. Actions often represent a single function or interface in an application. For
example, when users request www.example.com/posts/index (which is the same as www.example.com/posts/), theymight expect to see a listing of posts. The code for that action would look something like this:class PostsController extends AppController {public $helpers = array('Html', 'Form');
public function index() {$this->set('posts', $this->Post->find('all'));}}By defining function index() in our PostsController, users can access the logic there by requestingwww.example.com/posts/index. Similarly, if we were to define a function called foobar(), users would be able toaccess that at www.example.com/posts/foobar.
Warning: You may be tempted to name your controllers and actions a certain way to obtain a certain URL. Resistthat temptation. Follow CakePHP conventions (capitalization, plural names, etc.) and create readable, understandable action names. You can map URLs to your code using “routes” covered later on.
The single instruction in the action uses set() to pass data from the controller to the view (which we’ll create next).
The line sets the view variable called ‘posts’ equal to the return value of the find('all') method of the Post model.
Our Post model is automatically available at $this->Post because we’ve followed CakePHP’s naming conventions.
To learn more about CakePHP’s controllers, check out the Controllers chapter.

Creating Post Views
Now that we have our data flowing to our model, and our application logic and flow defined by our controller, let’screate a view for the index action we created above.
CakePHP views are just presentation-flavored fragments that fit inside an application’s layout. For most applications,they’re HTML mixed with PHP, but they may end up as XML, CSV, or even binary data.
A layout is presentation code that is wrapped around a view. Multiple layouts can be defined, and you can switch
between them, but for now, let’s just use the default.
Remember how in the last section we assigned the ‘posts’ variable to the view using the set() method? That would
pass data to the view that would look something like this:// print_r($posts) output:(continues on next page)

Blog Tutorial - Adding a layer

663

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)Array([0] => Array([Post] => Array([id] => 1[title] => The title[body] => This is the post body.
[created] => 2008-02-13 18:34:55[modified] =>))[1] => Array([Post] => Array([id] => 2[title] => A title once again[body] => And the post body follows.
[created] => 2008-02-13 18:34:56[modified] =>))[2] => Array([Post] => Array([id] => 3[title] => Title strikes back[body] => This is really exciting! Not.
[created] => 2008-02-13 18:34:57[modified] =>)))CakePHP’s view files are stored in /app/View inside a folder named after the controller to which they correspond.
(We’ll have to create a folder named ‘Posts’ in this case.) To format this post data into a nice table, our view code mightlook something like this<!-- File: /app/View/Posts/index.ctp --><h1>Blog posts</h1><table><tr><th>Id</th><th>Title</th><th>Created</th></tr><!-- Here is where we loop through our $posts array, printing out post info -->(continues on next page)

664

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)<?php foreach ($posts as $post): ?><tr><td><?php echo $post['Post']['id']; ?></td><td><?php echo $this->Html->link($post['Post']['title'],array('controller' => 'posts', 'action' => 'view', $post['Post']['id'])); ?></td><td><?php echo $post['Post']['created']; ?></td></tr><?php endforeach; ?><?php unset($post); ?></table>You might have noticed the use of an object called $this->Html. This is an instance of the CakePHP HtmlHelperclass. CakePHP comes with a set of view helpers that make things like linking, form output, JavaScript and AJAX
a snap. You can learn more about how to use them in Helpers, but what’s important to note here is that the link()
method will generate an HTML link with the given title (the first parameter) and URL (the second parameter).
When specifying URLs in CakePHP, it is recommended that you use the array format. This is explained in moredetail in the section on Routes. Using the array format for URLs allows you to take advantage of CakePHP’s reverse routing capabilities. You can also specify URLs relative to the base of the application in the form of /controller/action/param1/param2.
At this point, you should be able to point your browser to http://www.example.com/posts/index. You should see yourview, correctly formatted with the title and table listing of the posts.
If you happened to have clicked on one of the links we created in this view (which link a post’s title to a URL/posts/view/some_id), you were probably informed by CakePHP that the action hadn’t yet been defined. If you were
not so informed, either something has gone wrong, or you actually did define it already, in which case you are verysneaky. Otherwise, we’ll create it in the PostsController now:// File: /app/Controller/PostsController.php
class PostsController extends AppController {public $helpers = array('Html', 'Form');
public function index() {$this->set('posts', $this->Post->find('all'));}public function view($id = null) {if (!$id) {throw new NotFoundException(__('Invalid post'));}$post = $this->Post->findById($id);if (!$post) {throw new NotFoundException(__('Invalid post'));}$this->set('post', $post);}}
The set() call should look familiar. Notice we’re using findById() rather than find('all') because we only want

Blog Tutorial - Adding a layer

665

CakePHP Cookbook Documentation, Release 2.x

a single post’s information.
Notice that our view action takes a parameter: the ID of the post we’d like to see. This parameter is handed to the action
through the requested URL. If a user requests /posts/view/3, then the value ‘3’ is passed as $id.
We also do a bit of error checking to ensure that a user is actually accessing a record. If a user requests /posts/view,
we will throw a NotFoundException and let the CakePHP ErrorHandler take over. We also perform a similar checkto make sure the user has accessed a record that exists.
Now let’s create the view for our new ‘view’ action and place it in /app/View/Posts/view.ctp<!-- File: /app/View/Posts/view.ctp --><h1><?php echo h($post['Post']['title']); ?></h1><p><small>Created: <?php echo $post['Post']['created']; ?></small></p><p><?php echo h($post['Post']['body']); ?></p>
Verify that this is working by trying the links at /posts/index or manually requesting a post by accessing /posts/
view/1.

Adding Posts
Reading from the database and showing us the posts is a great start, but let’s allow for adding new posts.
First, start by creating an add() action in the PostsController:class PostsController extends AppController {public $helpers = array('Html', 'Form', 'Flash');public $components = array('Flash');public function index() {$this->set('posts', $this->Post->find('all'));}public function view($id) {if (!$id) {throw new NotFoundException(__('Invalid post'));}$post = $this->Post->findById($id);if (!$post) {throw new NotFoundException(__('Invalid post'));}$this->set('post', $post);}
public function add() {if ($this->request->is('post')) {$this->Post->create();if ($this->Post->save($this->request->data)) {$this->Flash->success(__('Your post has been saved.'));
return $this->redirect(array('action' => 'index'));}(continues on next page)

666

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$this->Flash->error(__('Unable to add your post.'));}}}

Note: $this->request->is() takes a single argument, which can be the request METHOD (get, put, post,
delete) or some request identifier (ajax). It is not a way to check for specific posted data. For instance,$this->request->is('book') will not return true if book data was posted.

Note: You need to include the FlashComponent - and FlashHelper - in any controller where you will use it. If
necessary, include it in your AppController.
Here’s what the add() action does: if the HTTP method of the request was POST, it tries to save the data using the Post
model. If for some reason it doesn’t save, it just renders the view. This gives us a chance to show the user validation
errors or other warnings.
Every CakePHP request includes a CakeRequest object which is accessible using $this->request. The request
object contains useful information regarding the request that was just received, and can be used to control the flow ofyour application. In this case, we use the CakeRequest::is() method to check that the request is a HTTP POST
request.
When a user uses a form to POST data to your application, that information is available in $this->request->data.
You can use the pr() or debug() functions to print it out if you want to see what it looks like.
We use the FlashComponent’s FlashComponent::success() method to set a message to a session variable to be
displayed on the page after redirection. In the layout we have FlashHelper::render() which displays the message
and clears the corresponding session variable. The controller’s Controller::redirect function redirects to another
URL. The param array('action' => 'index') translates to URL /posts (that is, the index action of the posts
controller). You can refer to Router::url() function on the API125 to see the formats in which you can specify a
URL for various CakePHP functions.
Calling the save() method will check for validation errors and abort the save if any occur. We’ll discuss how thoseerrors are handled in the following sections.
We call the create() method first in order to reset the model state for saving new information. It does not actuallycreate a record in the database, but clears Model::$id and sets Model::$data based on your database field defaults.

Data Validation
CakePHP goes a long way toward taking the monotony out of form input validation. Everyone hates coding up endlessforms and their validation routines. CakePHP makes it easier and faster.
To take advantage of the validation features, you’ll need to use CakePHP’s FormHelper in your views. The FormHelper
is available by default to all views at $this->Form.
Here’s our add view:<!-- File: /app/View/Posts/add.ctp --><h1>Add Post</h1>(continues on next page)
125 https://api.cakephp.org

Blog Tutorial - Adding a layer

667

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)<?php
echo $this->Form->create('Post');echo $this->Form->input('title');echo $this->Form->input('body', array('rows' => '3'));echo $this->Form->end('Save Post');?>We use the FormHelper to generate the opening tag for an HTML form.
$this->Form->create() generates:

Here’s the HTML that<form id="PostAddForm" method="post" action="/posts/add">
If create() is called with no parameters supplied, it assumes you are building a form that submits via POST to thecurrent controller’s add() action (or edit() action when id is included in the form data).
The $this->Form->input() method is used to create form elements of the same name. The first parameter tells
CakePHP which field they correspond to, and the second parameter allows you to specify a wide array of options - inthis case, the number of rows for the textarea. There’s a bit of introspection and automagic here: input() will output
different form elements based on the model field specified.
The $this->Form->end() call generates a submit button and ends the form. If a string is supplied as the first parameterto end(), the FormHelper outputs a submit button named accordingly along with the closing form tag. Again, refer toHelpers for more on helpers.
Now let’s go back and update our /app/View/Posts/index.ctp view to include a new “Add Post” link. Before the<table>, add the following line:<?php echo $this->Html->link('Add Post',
array('controller' => 'posts', 'action' => 'add')); ?>You may be wondering: how do I tell CakePHP about my validation requirements? Validation rules are defined in themodel. Let’s look back at our Post model and make a few adjustments:
class Post extends AppModel {public $validate = array('title' => array('rule' => 'notBlank'),'body' => array('rule' => 'notBlank'));}
The $validate array tells CakePHP how to validate your data when the save() method is called. Here, I’ve specified
that both the body and title fields must not be empty. CakePHP’s validation engine is strong, with a number of prebuilt rules (credit card numbers, email addresses, etc.) and flexibility for adding your own validation rules. For more
information, check the Data Validation.
Now that you have your validation rules in place, use the app to try to add a post with an empty title or body to see how
it works. Since we’ve used the FormHelper::input() method of the FormHelper to create our form elements, ourvalidation error messages will be shown automatically.

668

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x

Editing Posts
Post editing: here we go. You’re a CakePHP pro by now, so you should have picked up a pattern. Make the action, thenthe view. Here’s what the edit() action of the PostsController would look like:public function edit($id = null) {if (!$id) {throw new NotFoundException(__('Invalid post'));}$post = $this->Post->findById($id);if (!$post) {throw new NotFoundException(__('Invalid post'));}if ($this->request->is(array('post', 'put'))) {$this->Post->id = $id;
if ($this->Post->save($this->request->data)) {$this->Flash->success(__('Your post has been updated.'));
return $this->redirect(array('action' => 'index'));}$this->Flash->error(__('Unable to update your post.'));}
if (!$this->request->data) {$this->request->data = $post;}}
This action first ensures that the user has tried to access an existing record. If they haven’t passed in an $id parameter,or the post does not exist, we throw a NotFoundException for the CakePHP ErrorHandler to take care of.
Next the action checks whether the request is either a POST or a PUT request. If it is, then we use the POST data to
update our Post record, or kick back and show the user validation errors.
If there is no data set to $this->request->data, we simply set it to the previously retrieved post.
The edit view might look something like this:<!-- File: /app/View/Posts/edit.ctp --><h1>Edit Post</h1><?php
echo $this->Form->create('Post');echo $this->Form->input('title');echo $this->Form->input('body', array('rows' => '3'));echo $this->Form->input('id', array('type' => 'hidden'));echo $this->Form->end('Save Post');?>This view outputs the edit form (with the values populated), along with any necessary validation error messages.
One thing to note here: CakePHP will assume that you are editing a model if the ‘id’ field is present in the data array. If
no ‘id’ is present (look back at our add view), CakePHP will assume that you are inserting a new model when save()is called.
You can now update your index view with links to edit specific posts:
Blog Tutorial - Adding a layer

669

CakePHP Cookbook Documentation, Release 2.x<!-- File: /app/View/Posts/index.ctp(edit links added) --><h1>Blog posts</h1><p><?php echo $this->Html->link("Add Post", array('action' => 'add')); ?></p><table><tr><th>Id</th><th>Title</th><th>Action</th><th>Created</th></tr><!-- Here's where we loop through our $posts array, printing out post info --><?php foreach ($posts as $post): ?><tr><td><?php echo $post['Post']['id']; ?></td>
<td>
<?php
echo $this->Html->link($post['Post']['title'],array('action' => 'view', $post['Post']['id'])
);
?>
</td>
<td>
<?php
echo $this->Html->link('Edit',
array('action' => 'edit', $post['Post']['id'])
);
?>
</td>
<td>
<?php echo $post['Post']['created']; ?>
</td>
</tr>
<?php endforeach; ?></table>Deleting Posts
Next, let’s make a way for users to delete posts. Start with a delete() action in the PostsController:
public function delete($id) {if ($this->request->is('get')) {throw new MethodNotAllowedException();}if ($this->Post->delete($id)) {(continues on next page)

670

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$this->Flash->success(__('The post with id: %s has been deleted.', h($id)));} else {$this->Flash->error(__('The post with id: %s could not be deleted.', h($id)));}
return $this->redirect(array('action' => 'index'));}This logic deletes the post specified by $id, and uses $this->Flash->success() to show the user a confirmation
message after redirecting them on to /posts. If the user attempts to do a delete using a GET request, we throw an
Exception. Uncaught exceptions are captured by CakePHP’s exception handler, and a nice error page is displayed.
There are many built-in Exceptions that can be used to indicate the various HTTP errors your application might needto generate.
Because we’re just executing some logic and redirecting, this action has no view. You might want to update your index
view with links that allow users to delete posts, however:<!-- File: /app/View/Posts/index.ctp --><h1>Blog posts</h1><p><?php echo $this->Html->link('Add Post', array('action' => 'add')); ?></p><table><tr><th>Id</th><th>Title</th><th>Actions</th><th>Created</th></tr><!-- Here's where we loop through our $posts array, printing out post info --><?php foreach ($posts as $post): ?><tr><td><?php echo $post['Post']['id']; ?></td>
<td>
<?php
echo $this->Html->link($post['Post']['title'],array('action' => 'view', $post['Post']['id'])
);
?>
</td>
<td>
<?php
echo $this->Form->postLink('Delete',array('action' => 'delete', $post['Post']['id']),array('confirm' => 'Are you sure?'));(continues on next page)

Blog Tutorial - Adding a layer

671

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

?>
<?php
echo $this->Html->link('Edit', array('action' => 'edit', $post['Post']['id'])
);
?>
</td>
<td>
<?php echo $post['Post']['created']; ?>
</td>
</tr>
<?php endforeach; ?></table>Using postLink() will create a link that uses JavaScript to do a POST request to delete our post. Allowing content
to be deleted using GET requests is dangerous, as web crawlers could accidentally delete all your content.
Note: This view code also uses the FormHelper to prompt the user with a JavaScript confirmation dialog before theyattempt to delete a post.

RoutesFor some, CakePHP’s default routing works well enough. Developers who are sensitive to user-friendliness and generalsearch engine compatibility will appreciate the way that CakePHP’s URLs map to specific actions. So we’ll just makea quick change to routes in this tutorial.
For more information on advanced routing techniques, see Routes Configuration.
By default, CakePHP responds to a request for the root of your site (e.g., http://www.example.com) using its PagesController, rendering a view called “home”. Instead, we’ll replace this with our PostsController by creating a routing rule.
CakePHP’s routing is found in /app/Config/routes.php. You’ll want to comment out or remove the line that defines
the default root route. It looks like this:Router::connect('/',array('controller' => 'pages', 'action' => 'display', 'home'));
This line connects the URL ‘/’ with the default CakePHP home page. We want it to connect with our own controller,
so replace that line with this one:
Router::connect('/', array('controller' => 'posts', 'action' => 'index'));This should connect users requesting ‘/’ to the index() action of our PostsController.
Note: CakePHP also makes use of ‘reverse routing’. If, with the above route defined, you pass array('controller'=> 'posts', 'action' => 'index') to a function expecting an array, the resulting URL used will be ‘/’. It’s
therefore a good idea to always use arrays for URLs, as this means your routes define where a URL goes, and also
ensures that links point to the same place.

672

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x

Conclusion
Creating applications this way will win you peace, honor, love, and money beyond even your wildest fantasies. Simple,
isn’t it? Keep in mind that this tutorial was very basic. CakePHP has many more features to offer, and is flexible in ways
we didn’t wish to cover here for simplicity’s sake. Use the rest of this manual as a guide for building more feature-richapplications.
Now that you’ve created a basic CakePHP application, you’re ready for the real thing. Start your own project and readthe rest of the Cookbook and API126 .
If you need help, there are many ways to get the help you need - please see the Where to Get Help page. Welcome to
CakePHP!
Suggested Follow-up Reading
These are common tasks people learning CakePHP usually want to study next:
1. Layouts: Customizing your website layout
2. Elements: Including and reusing view snippets
3. Scaffolding: Prototyping before creating code
4. Code Generation with Bake: Generating basic CRUD code
5. Simple Authentication and Authorization Application: User authentication and authorization tutorial

Simple Authentication and Authorization Application
Following our Blog Tutorial example, imagine we wanted to secure the access to certain URLs, based on the logged
in user. We also have another requirement, to allow our blog to have multiple authors so each one of them can create
their own posts, edit and delete them at will disallowing other authors to make any changes on one’s posts.

Creating all users’ related code
First, let’s create a new table in our blog database to hold our users’ data:
CREATE TABLE users (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
password VARCHAR(255),
role VARCHAR(20),created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL);
We have adhered to the CakePHP conventions in naming tables, but we’re also taking advantage of another convention:by using the username and password columns in a users table, CakePHP will be able to auto configure most things for
us when implementing the user login.
Next step is to create our User model, responsible for finding, saving and validating any user data:
126 https://api.cakephp.org

Simple Authentication and Authorization Application

673

CakePHP Cookbook Documentation, Release 2.x// app/Model/User.php
App::uses('AppModel', 'Model');class User extends AppModel {public $validate = array('username' => array('required' => array('rule' => 'notBlank','message' => 'A username is required')),'password' => array('required' => array('rule' => 'notBlank','message' => 'A password is required')),'role' => array('valid' => array('rule' => array('inList', array('admin', 'author')),'message' => 'Please enter a valid role','allowEmpty' => false)));}
Let’s also create our UsersController, the following contents correspond to a basic baked UsersController class using
the code generation utilities bundled with CakePHP:// app/Controller/UsersController.php
App::uses('AppController', 'Controller');class UsersController extends AppController {public function beforeFilter() {parent::beforeFilter();$this->Auth->allow('add');}
public function index() {$this->User->recursive = 0;
$this->set('users', $this->paginate());}
public function view($id = null) {$this->User->id = $id;if (!$this->User->exists()) {throw new NotFoundException(__('Invalid user'));}$this->set('user', $this->User->findById($id));}(continues on next page)

674

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

public function add() {if ($this->request->is('post')) {$this->User->create();if ($this->User->save($this->request->data)) {$this->Flash->success(__('The user has been saved'));return $this->redirect(array('action' => 'index'));}$this->Flash->error(__('The user could not be saved. Please, try again.'));}}public function edit($id = null) {$this->User->id = $id;if (!$this->User->exists()) {throw new NotFoundException(__('Invalid user'));}if ($this->request->is('post') || $this->request->is('put')) {if ($this->User->save($this->request->data)) {$this->Flash->success(__('The user has been saved'));return $this->redirect(array('action' => 'index'));}$this->Flash->error(__('The user could not be saved. Please, try again.'));} else {$this->request->data = $this->User->findById($id);
unset($this->request->data['User']['password']);}}public function delete($id = null) {// Prior to 2.5 use// $this->request->onlyAllow('post');$this->request->allowMethod('post');$this->User->id = $id;if (!$this->User->exists()) {throw new NotFoundException(__('Invalid user'));}if ($this->User->delete()) {$this->Flash->success(__('User deleted'));return $this->redirect(array('action' => 'index'));}$this->Flash->error(__('User was not deleted'));return $this->redirect(array('action' => 'index'));}}Changed in version 2.5: Since 2.5, use CakeRequest::allowMethod() instead of CakeRequest::onlyAllow()
Simple Authentication and Authorization Application

675

CakePHP Cookbook Documentation, Release 2.x(deprecated).
In the same way we created the views for our blog posts or by using the code generation tool, we implement the views.
For the purpose of this tutorial, we will show just the add.ctp:<!-- app/View/Users/add.ctp --><div class="users form"><?php echo $this->Form->create('User'); ?><fieldset><legend><?php echo __('Add User'); ?></legend><?php echo $this->Form->input('username');echo $this->Form->input('password');echo $this->Form->input('role', array('options' => array('admin' => 'Admin', 'author' => 'Author')));?></fieldset><?php echo $this->Form->end(__('Submit')); ?></div>

Authentication (login and logout)
We’re now ready to add our authentication layer. In CakePHP this is handled by the AuthComponent, a class responsible for requiring login for certain actions, handling user sign-in and sign-out, and also authorizing logged in users to
the actions they are allowed to reach.
To add this component to your application open your app/Controller/AppController.php file and add the following lines:// app/Controller/AppController.php
class AppController extends Controller {//...
public $components = array('Flash','Auth' => array('loginRedirect' => array('controller' => 'posts','action' => 'index'),'logoutRedirect' => array('controller' => 'pages','action' => 'display','home'),'authenticate' => array('Form' => array('passwordHasher' => 'Blowfish'))));(continues on next page)

676

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

public function beforeFilter() {$this->Auth->allow('index', 'view');
}
//...
}
There is not much to configure, as we used the conventions for the users table. We just set up the URLs that will be
loaded after the login and logout actions is performed, in our case to /posts/ and / respectively.
What we did in the beforeFilter function was to tell the AuthComponent to not require a login for all index andview actions, in every controller. We want our visitors to be able to read and list the entries without registering in thesite.
Now, we need to be able to register new users, save their username and password, and, more importantly, hash theirpassword so it is not stored as plain text in our database. Let’s tell the AuthComponent to let un-authenticated users
access the users add function and implement the login and logout action:// app/Controller/UsersController.php
public function beforeFilter() {parent::beforeFilter();// Allow users to register and logout.
$this->Auth->allow('add', 'logout');}
public function login() {if ($this->request->is('post')) {if ($this->Auth->login()) {return $this->redirect($this->Auth->redirectUrl());}$this->Flash->error(__('Invalid username or password, try again'));}}public function logout() {return $this->redirect($this->Auth->logout());}
Password hashing is not done yet, open your app/Model/User.php model file and add the following:// app/Model/User.php
App::uses('AppModel', 'Model');App::uses('BlowfishPasswordHasher', 'Controller/Component/Auth');class User extends AppModel {// ...
public function beforeSave($options = array()) {if (isset($this->data[$this->alias]['password'])) {$passwordHasher = new BlowfishPasswordHasher();$this->data[$this->alias]['password'] = $passwordHasher->hash((continues on next page)

Simple Authentication and Authorization Application

677

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$this->data[$this->alias]['password']);}
return true;}// ...

Note: The BlowfishPasswordHasher uses a stronger hashing algorithm (bcrypt) than SimplePasswordHasher (sha1)
and provides per user salts. The SimplePasswordHasher will be removed as of CakePHP version 3.0
So, now every time a user is saved, the password is hashed using the BlowfishPasswordHasher class. We’re just missing
a template view file for the login function. Open up your app/View/Users/login.ctp file and add the following
lines://app/View/Users/login.ctp<div class="users form"><?php echo $this->Flash->render('auth'); ?><?php echo $this->Form->create('User'); ?><fieldset><legend><?php echo __('Please enter your username and password'); ?></legend><?php echo $this->Form->input('username');echo $this->Form->input('password');?></fieldset><?php echo $this->Form->end(__('Login')); ?></div>
You can now register a new user by accessing the /users/add URL and log-in with the newly created credentials bygoing to /users/login URL. Also try to access any other URL that was not explicitly allowed such as /posts/add,
you will see that the application automatically redirects you to the login page.
And that’s it! It looks too simple to be truth. Let’s go back a bit to explain what happened. The beforeFilter function
is telling the AuthComponent to not require a login for the add action in addition to the index and view actions thatwere already allowed in the AppController’s beforeFilter function.
The login action calls the $this->Auth->login() function in the AuthComponent, and it works without any further
config because we are following conventions as mentioned earlier. That is, having a User model with a username anda password column, and use a form posted to a controller with the user data. This function returns whether the login
was successful or not, and in the case it succeeds, then we redirect the user to the configured redirection URL that weused when adding the AuthComponent to our application.
The logout works by just accessing the /users/logout URL and will redirect the user to the configured logoutUrlformerly described. This URL is the result of the AuthComponent::logout() function on success.

678

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x

Authorization (who’s allowed to access what)
As stated before, we are converting this blog into a multi-user authoring tool, and in order to do this, we need to modify
the posts table a bit to add the reference to the User model:
ALTER TABLE posts ADD COLUMN user_id INT(11);Also, a small change in the PostsController is required to store the currently logged in user as a reference for the createdpost:// app/Controller/PostsController.php
public function add() {if ($this->request->is('post')) {//Added this line
$this->request->data['Post']['user_id'] = $this->Auth->user('id');
if ($this->Post->save($this->request->data)) {$this->Flash->success(__('Your post has been saved.'));
return $this->redirect(array('action' => 'index'));}}}The user() function provided by the component returns any column from the currently logged in user. We used thismethod to add the data into the request info that is saved.
Let’s secure our app to prevent some authors from editing or deleting the others’ posts. Basic rules for our app are thatadmin users can access every URL, while normal users (the author role) can only access the permitted actions. Openagain the AppController class and add a few more options to the Auth config:// app/Controller/AppController.php
public $components = array('Flash','Auth' => array('loginRedirect' => array('controller' => 'posts', 'action' => 'index'),'logoutRedirect' => array('controller' => 'pages','action' => 'display','home'),'authenticate' => array('Form' => array('passwordHasher' => 'Blowfish')),'authorize' => array('Controller') // Added this line));
public function isAuthorized($user) {// Admin can access every action
if (isset($user['role']) && $user['role'] === 'admin') {return true;}(continues on next page)

Simple Authentication and Authorization Application

679

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// Default denyreturn false;}
We just created a very simple authorization mechanism. In this case the users with role admin will be able to accessany URL in the site when logged in, but the rest of them (i.e the role author) can’t do anything different from notlogged in users.
This is not exactly what we wanted, so we need to supply more rules to our isAuthorized() method. But insteadof doing it in AppController, let’s delegate each controller to supply those extra rules. The rules we’re going to add to
PostsController should allow authors to create posts but prevent the edition of posts if the author does not match. Openthe file PostsController.php and add the following content:// app/Controller/PostsController.php
public function isAuthorized($user) {// All registered users can add posts
if ($this->action === 'add') {return true;}// The owner of a post can edit and delete it
if (in_array($this->action, array('edit', 'delete'))) {$postId = (int) $this->request->params['pass'][0];if ($this->Post->isOwnedBy($postId, $user['id'])) {return true;}}
return parent::isAuthorized($user);}
We’re now overriding the AppController’s isAuthorized() call and internally checking if the parent class is alreadyauthorizing the user. If he isn’t, then just allow him to access the add action, and conditionally access edit and delete.
A final thing is left to be implemented, to tell whether the user is authorized to edit the post or not, we’re calling a
isOwnedBy() function in the Post model. It is in general a good practice to move as much logic as possible intomodels. Let’s then implement the function:// app/Model/Post.php
public function isOwnedBy($post, $user) {return $this->field('id', array('id' => $post, 'user_id' => $user)) !== false;}
This concludes our simple authentication and authorization tutorial. For securing the UsersController you can follow
the same technique we did for PostsController. You could also be more creative and code something more general in
AppController based on your own rules.
Should you need more control, we suggest you read the complete Auth guide in the Authentication section where you
will find more about configuring the component, creating custom Authorization classes, and much more.

680

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x

Suggested Follow-up Reading
1. Code Generation with Bake Generating basic CRUD code
2. Authentication: User registration and login

Simple Acl controlled Application
Note: This isn’t a beginner level tutorial. If you are just starting out with CakePHP we would advise you to get a betteroverall experience of the framework’s features before trying out this tutorial.
In this tutorial you will create a simple application with Authentication and Access Control Lists. This tutorial assumes
you have read the Blog Tutorial and you are familiar with Code Generation with Bake. You should have some experience
with CakePHP, and be familiar with MVC concepts. This tutorial is a brief introduction to the AuthComponent and
AclComponent.
What you will need
1. A running web server. We’re going to assume you’re using Apache, though the instructions for using otherservers should be very similar. We might have to play a little with the server configuration, but most folks can
get CakePHP up and running without any configuration at all.
2. A database server. We’re going to be using MySQL in this tutorial. You’ll need to know enough about SQL inorder to create a database: CakePHP will be taking the reins from there.
3. Basic PHP knowledge. The more object-oriented programming you’ve done, the better: but fear not if you’re aprocedural fan.

Preparing our Application
First, let’s get a copy of fresh CakePHP code.
To get a fresh download, visit the CakePHP project at GitHub: https://github.com/cakephp/cakephp/tags and download
the stable release. For this tutorial you need the latest 2.0 release.
You can also clone the repository using git127 :git clone -b 2.x git://github.com/cakephp/cakephp.git
Once you’ve got a copy of CakePHP latest 2.0 release, setup your database.php config file, and change the valueof Security.salt in your app/Config/core.php. From there we will build a simple database schema to build ourapplication on. Execute the following SQL statements into your database:
CREATE TABLE users (id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
password CHAR(40) NOT NULL,
group_id INT(11) NOT NULL,
created DATETIME,
modified DATETIME);(continues on next page)
127 https://git-scm.com/

Simple Acl controlled Application

681

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

CREATE TABLE groups (id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
created DATETIME,
modified DATETIME);CREATE TABLE posts (id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id INT(11) NOT NULL,
title VARCHAR(255) NOT NULL,
body TEXT,
created DATETIME,
modified DATETIME);CREATE TABLE widgets (id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
part_no VARCHAR(12),quantity INT(11));
These are the tables we will be using to build the rest of our application. Once we have the table structure in the database
we can start cooking. Use Code Generation with Bake to quickly create your models, controllers, and views.
To use cake bake, call cake bake all and this will list the 4 tables you inserted into MySQL. Select “1. Group”, andfollow the prompts. Repeat for the other 3 tables, and this will have generated the 4 controllers, models and your viewsfor you.
Avoid using Scaffold here. The generation of the ACOs will be seriously affected if you bake the controllers with the
Scaffold feature.
While baking the Models cake will automagically detect the associations between your Models (or relations between
your tables). Let cake supply the correct hasMany and belongsTo associations. If you are prompted to pick hasOne or
hasMany, generally speaking you’ll need a hasMany (only) relationships for this tutorial.
Leave out admin routing for now, this is a complicated enough subject without them. Also be sure not to add either
the Acl or Auth Components to any of your controllers as you are baking them. We’ll be doing that soon enough. You
should now have models, controllers, and baked views for your users, groups, posts and widgets.

Preparing to Add Auth
We now have a functioning CRUD application. Bake should have setup all the relations we need, otherwise add themright now. There are a few other pieces that need to be added before we can add the Auth and Acl components. First
add a login and logout action to your UsersController:
public function login() {if ($this->request->is('post')) {if ($this->Auth->login()) {return $this->redirect($this->Auth->redirectUrl());}$this->Session->setFlash(__('Your username or password was incorrect.'));(continues on next page)682

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}}
public function logout() {//Leave empty for now.
}
Then create the following view file for login at app/View/Users/login.ctp:<?php
echo $this->Form->create('User', array('action' => 'login'));echo $this->Form->inputs(array('legend' => __('Login'),'username','password'));echo $this->Form->end('Login');?>
Next we’ll have to update our User model to hash passwords before they go into the database. Storing plaintext passwords is extremely insecure and AuthComponent will expect that your passwords are hashed. In app/Model/User.
php add the following:
App::uses('AuthComponent', 'Controller/Component');class User extends AppModel {// other code.
public function beforeSave($options = array()) {$this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
return true;}}
Next we need to make some modifications to AppController.
If you don’t have /app/Controller/
AppController.php, create it. Since we want our entire site controlled with Auth and Acl, we will set them up
in AppController:class AppController extends Controller {public $components = array('Acl','Auth' => array('authorize' => array('Actions' => array('actionPath' => 'controllers'))),'Session');public $helpers = array('Html', 'Form', 'Session');public function beforeFilter() {//Configure AuthComponent(continues on next page)

Simple Acl controlled Application

683

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$this->Auth->loginAction = array('controller' => 'users','action' => 'login');$this->Auth->logoutRedirect = array('controller' => 'users','action' => 'login');$this->Auth->loginRedirect = array('controller' => 'posts','action' => 'add');}}
Before we set up the ACL at all we will need to add some users and groups. With AuthComponent in use we will not
be able to access any of our actions, as we are not logged in. We will now add some exceptions so AuthComponent
will allow us to create some groups and users. In both your GroupsController and your UsersController Add
the following:
public function beforeFilter() {parent::beforeFilter();// For CakePHP 2.0$this->Auth->allow('*');// For CakePHP 2.1 and up$this->Auth->allow();}
These statements tell AuthComponent to allow public access to all actions. This is only temporary and will be removed
once we get a few users and groups into our database. Don’t add any users or groups just yet though.

Initialize the Db Acl tables
Before we create any users or groups we will want to connect them to the Acl. However, we do not at this time have
any Acl tables and if you try to view any pages right now, you may get a missing table error (“Error: Database tableacos for model Aco was not found.”). To remove these errors we need to run a schema file. In a shell run the following:./Console/cake schema create DbAcl
This schema will prompt you to drop and create the tables. Say yes to dropping and creating the tables.
If you don’t have shell access, or are having trouble using the console, you can run the sql file found in/path/to/app/Config/Schema/db_acl.sql.
With the controllers setup for data entry, and the Acl tables initialized we are ready to go right? Not entirely, we still
have a bit of work to do in the user and group models. Namely, making them auto-magically attach to the Acl.

684

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x

Acts As a Requester
For Auth and Acl to work properly we need to associate our users and groups to rows in the Acl tables. In order todo this we will use the AclBehavior. The AclBehavior allows for the automagic connection of models with the
Acl tables. Its use requires an implementation of parentNode() on your model. In our User model we will add thefollowing:class User extends AppModel {public $belongsTo = array('Group');public $actsAs = array('Acl' => array('type' => 'requester'));public function parentNode() {if (!$this->id && empty($this->data)) {return null;}
if (isset($this->data['User']['group_id'])) {$groupId = $this->data['User']['group_id'];} else {$groupId = $this->field('group_id');}
if (!$groupId) {return null;}
return array('Group' => array('id' => $groupId));}}
Then in our Group Model Add the following:class Group extends AppModel {public $actsAs = array('Acl' => array('type' => 'requester'));public function parentNode() {return null;}}
What this does, is tie the Group and User models to the Acl, and tell CakePHP that every-time you make a User orGroup you want an entry on the aros table as well. This makes Acl management a piece of cake as your AROs become
transparently tied to your users and groups tables. So anytime you create or delete a user/group the Aro table isupdated.
Our controllers and models are now prepared for adding some initial data, and our Group and User models are boundto the Acl table. So add some groups and users using the baked forms by browsing to http://example.com/groups/add
and http://example.com/users/add. I made the following groups:• administrators• managers• users
I also created a user in each group so I had a user of each different access group to test with later. Write everything down
or use easy passwords so you don’t forget. If you do a SELECT * FROM aros; from a MySQL prompt you should getsomething like the following:

Simple Acl controlled Application

685

CakePHP Cookbook Documentation, Release 2.x+----+-----------+-------+-------------+-------+------+------+| id | parent_id | model | foreign_key | alias | lft | rght |+----+-----------+-------+-------------+-------+------+------+| 1 |NULL | Group |1 | NULL |
1 |
4 |
| 2 |
NULL | Group |2 | NULL |
5 |
8 |
| 3 |
NULL | Group |3 | NULL |
9 |
12 |
| 4 |
1 | User |1 | NULL |
2 |
3 |
| 5 |
2 | User |2 | NULL |
6 |
7 |
| 6 |
3 | User |3 | NULL |
10 |
11 |
+----+-----------+-------+-------------+-------+------+------+
6 rows in set (0.00 sec)
This shows us that we have 3 groups and 3 users. The users are nested inside the groups, which means we can set
permissions on a per-group or per-user basis.
Group-only ACL
In case we want simplified per-group only permissions, we need to implement bindNode() in User model:
public function bindNode($user) {return array('model' => 'Group', 'foreign_key' => $user['User']['group_id']);}Then modify the actsAs for the model User and disable the requester directive:
public $actsAs = array('Acl' => array('type' => 'requester', 'enabled' => false));
These two changes will tell ACL to skip checking User Aro’s and to check only Group Aro’s. This also avoids theafterSave being called.
Note: Every user has to have group_id assigned for this to work.
Now the aros table will look like this:+----+-----------+-------+-------------+-------+------+------+| id | parent_id | model | foreign_key | alias | lft | rght |+----+-----------+-------+-------------+-------+------+------+| 1 |NULL | Group |1 | NULL |
1 |
2 |
| 2 |
NULL | Group |2 | NULL |
3 |
4 |
| 3 |
NULL | Group |3 | NULL |5 |6 |+----+-----------+-------+-------------+-------+------+------+
3 rows in set (0.00 sec)
Note: If you have followed the tutorial up to this point you need to drop your tables, including aros, groups andusers, and create the groups and users again from scratch in order to get the aros table seen above.

686

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x

Creating ACOs (Access Control Objects)
Now that we have our users and groups (aros), we can begin inputting our existing controllers into the Acl and setting
permissions for our groups and users, as well as enabling login / logout.
Our ARO are automatically creating themselves when new users and groups are created. What about a way to autogenerate ACOs from our controllers and their actions? Well unfortunately there is no magic way in CakePHP’s coreto accomplish this. The core classes offer a few ways to manually create ACO’s though. You can create ACO objects
from the Acl shell or You can use the AclComponent. Creating Acos from the shell looks like:./Console/cake acl create aco root controllers
While using the AclComponent would look like:$this->Acl->Aco->create(array('parent_id' => null, 'alias' => 'controllers'));$this->Acl->Aco->save();
Both of these examples would create our ‘root’ or top level ACO which is going to be called ‘controllers’. The purposeof this root node is to make it easy to allow/deny access on a global application scope, and allow the use of the Acl for
purposes not related to controllers/actions such as checking model record permissions. As we will be using a global
root ACO we need to make a small modification to our AuthComponent configuration. AuthComponent needs toknow about the existence of this root node, so that when making ACL checks it can use the correct node path whenlooking up controllers/actions. In AppController ensure that your $components array contains the actionPathdefined earlier:class AppController extends Controller {public $components = array('Acl','Auth' => array('authorize' => array('Actions' => array('actionPath' => 'controllers'))),'Session');
Continue to Simple Acl controlled Application - part 2 to continue the tutorial.

Simple Acl controlled Application - part 2
An Automated tool for creating ACOs
As mentioned before, there is no pre-built way to input all of our controllers and actions into the Acl. However, we allhate doing repetitive things like typing in what could be hundreds of actions in a large application.
For this purpose exists a very handy plugin available on GitHub, called AclExtras128 which can be downloaded in The
GitHub Downloads page129 . We’re going to briefly describe how to use it to generate all our ACO’sFirst grab a copy of the plugin and unzipped or clone it using git into app/Plugin/AclExtras. Then activate the plugin
in your app/Config/boostrap.php file as shown below:
128 https://github.com/markstory/acl_extras/

129 https://github.com/markstory/acl_extras/zipball/master

Simple Acl controlled Application - part 2687

CakePHP Cookbook Documentation, Release 2.x//app/Config/boostrap.php// ...
CakePlugin::load('AclExtras');Finally execute the following command in the CakePHP console:./Console/cake AclExtras.AclExtras aco_sync
You can get a complete guide for all available commands like this:./Console/cake AclExtras.AclExtras -h./Console/cake AclExtras.AclExtras aco_sync -hOnce populated your acos table proceed to create your application permissions.

Setting up permissions
Creating permissions much like creating ACO’s has no magic solution, nor will I be providing one. To allow ARO’saccess to ACO’s from the shell interface use the AclShell. For more information on how to use it consult the AclShell
help which can be accessed by running:./Console/cake acl --help
Note: * needs to be quoted (’*’)
In order to allow with the AclComponent we would use the following code syntax in a custom method:$this->Acl->allow($aroAlias, $acoAlias);
We are going to add in a few allow/deny statements now. Add the following to a temporary function in yourUsersController and visit the address in your browser to run them (e.g. http://localhost/cake/app/users/initdb).
If you do a SELECT * FROM aros_acos you should see a whole pile of 1’s and -1’s. Once you’ve confirmed yourpermissions are set, remove the function:
public function beforeFilter() {parent::beforeFilter();$this->Auth->allow('initDB'); // We can remove this line after we're finished}public function initDB() {$group = $this->User->Group;// Allow admins to everything$group->id = 1;
$this->Acl->allow($group, 'controllers');// allow managers to posts and widgets$group->id = 2;
$this->Acl->deny($group, 'controllers');$this->Acl->allow($group, 'controllers/Posts');$this->Acl->allow($group, 'controllers/Widgets');// allow users to only add and edit on posts and widgets(continues on next page)

688

Chapter 12. Tutorials & Examples

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$group->id = 3;
$this->Acl->deny($group, 'controllers');$this->Acl->allow($group, 'controllers/Posts/add');$this->Acl->allow($group, 'controllers/Posts/edit');$this->Acl->allow($group, 'controllers/Widgets/add');$this->Acl->allow($group, 'controllers/Widgets/edit');// allow basic users to log out$this->Acl->allow($group, 'controllers/users/logout');// we add an exit to avoid an ugly "missing views" error message
echo "all done";exit;}
We now have set up some basic access rules. We’ve allowed administrators to everything. Managers can access everything in posts and widgets. While users can only access add and edit in posts & widgets.
We had to get a reference of a Group model and modify its id to be able to specify the ARO we wanted, this is due
to how AclBehavior works. AclBehavior does not set the alias field in the aros table so we must use an objectreference or an array to reference the ARO we want.
You may have noticed that I deliberately left out index and view from my Acl permissions. We are going to
make view and index public actions in PostsController and WidgetsController. This allows non-authorizedusers to view these pages, making them public pages. However, at any time you can remove these actions from
AuthComponent::allowedActions and the permissions for view and edit will revert to those in the Acl.
Now we want to take out the references to Auth->allowedActions in your users and groups controllers. Then add
the following to your posts and widgets controllers:public function beforeFilter() {parent::beforeFilter();$this->Auth->allow('index', 'view');}
This removes the ‘off switches’ we put in earlier on the users and groups controllers, and gives public access on theindex and view actions in posts and widgets controllers. In AppController::beforeFilter() add the following:$this->Auth->allow('display');This makes the ‘display’ action public. This will keep our PagesController::display() public. This is important as often
the default routing has this action as the home page for your application.

Logging in
Our application is now under access control, and any attempt to view non-public pages will redirect you to the login
page. However, we will need to create a login view before anyone can login. Add the following to app/View/Users/
login.ctp if you haven’t done so already:<h2>Login</h2><?php
echo $this->Form->create('User', array('url' => array((continues on next page)

Simple Acl controlled Application - part 2689

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'controller' => 'users','action' => 'login')));echo $this->Form->input('User.username');echo $this->Form->input('User.password');echo $this->Form->end('Login');If a user is already logged in, redirect him by adding this to your UsersController:
public function login() {if ($this->Session->read('Auth.User')) {$this->Session->setFlash('You are logged in!');return $this->redirect('/');}}You should now be able to login and everything should work auto-magically. When access is denied Auth messages
will be displayed if you added the echo $this->Session->flash('auth')

LogoutNow onto the logout. Earlier we left this function blank, now is the time to fill it. In UsersController::logout()
add the following:$this->Session->setFlash('Good-Bye');$this->redirect($this->Auth->logout());
This sets a Session flash message and logs out the User using Auth’s logout method. Auth’s logout method basicallydeletes the Auth Session Key and returns a URL that can be used in a redirect. If there is other session data that needs
to be deleted as well add that code here.

All done
You should now have an application controlled by Auth and Acl. Users permissions are set at the group level, but you
can set them by user at the same time. You can also set permissions on a global and per-controller and per-action basis.
Furthermore, you have a reusable block of code to easily expand your ACO table as your app grows.

690

Chapter 12. Tutorials & Examples

CHAPTER 13

Contributing

There are a number of ways you can contribute to CakePHP. The following sections cover the various ways you cancontribute to CakePHP:

Documentation
Contributing to the documentation is simple. The files are hosted on https://github.com/cakephp/docs. Feel free tofork the repo, add your changes/improvements/translations and give back by issuing a pull request. You can even edit
the docs online with GitHub, without ever downloading the files – the “Improve this Doc” button on any given page
will direct you to GitHub’s online editor for that page.
The CakePHP documentation is continuously integrated130 , so you can check the status of the various builds131 on the
Jenkins server at any time.

Translations
Email the docs team (docs at cakephp dot org) or hop on IRC (#cakephp on freenode) to discuss any translation efforts
you would like to participate in.
130 https://en.wikipedia.org/wiki/Continuous_integration
131 https://ci.cakephp.org

691

CakePHP Cookbook Documentation, Release 2.x

New Translation Language
We want to provide translations that are as complete as possible. However, there may be times where a translation fileis not up-to-date. You should always consider the English version as the authoritative version.
If your language is not in the current languages, please contact us through Github and we will consider creating askeleton folder for it. The following sections are the first one you should consider translating as these files don’t change
often:• index.rst• cakephp-overview.rst• getting-started.rst• installation.rst• /installation folder• /getting-started folder• /tutorials-and-examples folder
Reminder for Docs Administrators
The structure of all language folders should mirror the English folder structure. If the structure changes for the English
version, we should apply those changes in the other languages.
For example, if a new English file is created in en/file.rst, we should:• Add the file in all other languages : fr/file.rst, zh/file.rst, . . .
• Delete the content, but keeping the title, meta information and eventual toc-tree elements. The following
note will be added while nobody has translated the file:
File Title##########.. note::The documentation is not currently supported in XX language for thispage.
Please feel free to send us a pull request on`Github <https://github.com/cakephp/docs>`_ or use the **Improve This Doc**button to directly propose your changes.
You can refer to the English version in the select top menu to haveinformation about this page's topic.
// If toc-tree elements are in the English version.. toctree:::maxdepth: 1
one-toc-fileother-toc-file.. meta::(continues on next page)

692

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x(continued from previous page):title lang=xx: File Title:keywords lang=xx: title, description,...

Translator tips
• Browse and edit in the language you want the content to be translated to - otherwise you won’t see what has
already been translated.
• Feel free to dive right in if your chosen language already exists on the book.
• Use Informal Form132 .
• Translate both the content and the title at the same time.
• Do compare to the English content before submitting a correction (if you correct something, but don’t integratean ‘upstream’ change your submission won’t be accepted).
• If you need to write an English term, wrap it in <em> tags. E.g. “asdf asdf Controller asdf” or “asdf asdf
Kontroller (Controller) asfd” as appropriate.
• Do not submit partial translations.
• Do not edit a section with a pending change.
• Do not use html entities133 for accented characters, the book uses UTF-8.
• Do not significantly change the markup (HTML) or add new content• If the original content is missing some info, submit an edit for that first.

Documentation Formatting Guide
The new CakePHP documentation is written with ReST formatted text134 . ReST (Re Structured Text) is a plain text
markup syntax similar to markdown, or textile. To maintain consistency it is recommended that when adding to the
CakePHP documentation you follow the guidelines here on how to format and structure your text.
Line Length
Lines of text should be wrapped at 80 columns. The only exception should be long URLs, and code snippets.
Headings and Sections
Section headers are created by underlining the title with punctuation characters at least the length of the text.
• # Is used to denote page titles.
• = Is used for sections in a page.
• - Is used for subsections.
• ~ Is used for sub-subsections• ^ Is used for sub-sub-sections.
132 https://en.wikipedia.org/wiki/Register_(linguistics)

133 https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
134 https://en.wikipedia.org/wiki/ReStructuredText

Documentation

693

CakePHP Cookbook Documentation, Release 2.x

Headings should not be nested more than 5 levels deep. Headings should be preceded and followed by a blank line.
Paragraphs
Paragraphs are simply blocks of text, with all the lines at the same level of indentation. Paragraphs should be separated
by more than one empty line.
Inline Markup• One asterisk: text for emphasis (italics) We’ll use it for general highlighting/emphasis.
– *text*.
• Two asterisks: text for strong emphasis (boldface) We’ll use it for working directories, bullet list subject, tablenames and excluding the following word “table”.
– **/config/Migrations**, **articles**, etc.
• Two backquotes: text for code samples We’ll use it for names of method options, names of table columns,
object names, excluding the following word “object” and for method/function names – include “()”.
– ``cascadeCallbacks``, ``true``, ``id``, ``PagesController``, ``config()``, etc.
If asterisks or backquotes appear in running text and could be confused with inline markup delimiters, they have to beescaped with a backslash.
Inline markup has a few restrictions:• It may not be nested.
• Content may not start or end with whitespace: * text* is wrong.
• Content must be separated from surrounding text by non-word characters. Use a backslash escaped space towork around that: onelong\ *bolded*\ word.
Lists
List markup is very similar to markdown. Unordered lists are indicated by starting a line with a single asterisk and aspace. Numbered lists can be created with either numerals, or # for auto numbering:* This is a bullet* So is this. But this line
has two lines.
1. First line
2. Second line#. Automatic numbering#. Will save you some time.
Indented lists can also be created, by indenting sections and separating them with an empty line:* First line
* Second line
* Going deeper(continues on next page)

694

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)* Whoah* Back to the first level.
Definition lists can be created by doing the following:
term
definition
CakePHP
An MVC framework for PHP
Terms cannot be more than one line, but definitions can be multi-line and all lines should be indented consistently.
Links
There are several kinds of links, each with their own uses.
External Links
Links to external documents can be with the following:`External Link <http://example.com>`_
The above would generate a link pointing to http://example.com
Links to Other Pages:doc:
Other pages in the documentation can be linked to using the :doc: role. You can link to the specified document
using either an absolute or relative path reference. You should omit the .rst extension. For example, if the reference :doc:`form` appears in the document core-helpers/html, then the link references core-helpers/
form. If the reference was :doc:`/core-helpers`, it would always reference /core-helpers regardless of
where it was used.
Cross Referencing Links:ref:
You can cross reference any arbitrary title in any document using the :ref: role. Link label targets must be
unique across the entire documentation. When creating labels for class methods, it’s best to use class-method
as the format for your link label.
The most common use of labels is above a title. Example:.. _label-name:
Section heading--------------More content here.

Documentation

695

CakePHP Cookbook Documentation, Release 2.x

Elsewhere you could reference the above section using :ref:`label-name`. The link’s text would be the title
that the link preceded. You can also provide custom link text using :ref:`Link text <label-name>`.
Prevent Sphinx to Output Warnings
Sphinx will output warnings if a file is not referenced in a toc-tree. It’s a great way to ensure that all files have a link
directed to them, but sometimes, you don’t need to insert a link for a file, eg. for our epub-contents and pdf-contents
files. In those cases, you can add :orphan: at the top of the file, to suppress warnings that the file is not in the toc-tree.
Describing Classes and their Contents
The CakePHP documentation uses the phpdomain135 to provide custom directives for describing PHP objects andconstructs. Using these directives and roles is required to give proper indexing and cross referencing features.
Describing Classes and Constructs
Each directive populates the index, and or the namespace index.
.. php:global:: name
This directive declares a new PHP global variable.
.. php:function:: name(signature)
Defines a new global function outside of a class.
.. php:const:: name
This directive declares a new PHP constant, you can also use it nested inside a class directive to create classconstants.
.. php:exception:: name
This directive declares a new Exception in the current namespace. The signature can include constructor arguments.
.. php:class:: name
Describes a class. Methods, attributes, and constants belonging to the class should be inside this directive’s body:.. php:class:: MyClassClass description.. php:method:: method($argument)
Method description
Attributes, methods and constants don’t need to be nested. They can also just follow the class declaration:.. php:class:: MyClass
Text about the class.. php:method:: methodName()
Text about the method
135 https://pypi.python.org/pypi/sphinxcontrib-phpdomain

696

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x

See also:
php:method, php:attr, php:const.. php:method:: name(signature)
Describe a class method, its arguments, return value, and exceptions:.. php:method:: instanceMethod($one, $two):param string $one: The first parameter.
:param string $two: The second parameter.
:returns: An array of stuff.
:throws: InvalidArgumentException
This is an instance method.
.. php:staticmethod:: ClassName::methodName(signature)
Describe a static method, its arguments, return value and exceptions, see php:method for options.
.. php:attr:: name
Describe an property/attribute on a class.
Prevent Sphinx to Output Warnings
Sphinx will output warnings if a function is referenced in multiple files. It’s a great way to ensure that you did not add
a function two times, but sometimes, you actually want to write a function in two or more files, eg. debug object isreferenced in /development/debugging and in /core-libraries/global-constants-and-functions. In this case, you can add:noindex: under the function debug to suppress warnings. Keep only one reference without :no-index: to still
have the function referenced:.. php:function:: debug(mixed $var, boolean $showHtml = null, $showFrom = true):noindex:

Cross Referencing
The following roles refer to PHP objects and links are generated if a matching directive is found::php:func:
Reference a PHP function.
:php:global:
Reference a global variable whose name has $ prefix.
:php:const:
Reference either a global constant, or a class constant. Class constants should be preceded by the owning class:
DateTime has an :php:const:`DateTime::ATOM` constant.
:php:class:
Reference a class by name::php:class:`ClassName`

Documentation

697

CakePHP Cookbook Documentation, Release 2.x:php:meth:
Reference a method of a class. This role supports both kinds of methods::php:meth:`DateTime::setDate`:php:meth:`Classname::staticMethod`:php:attr:
Reference a property on an object::php:attr:`ClassName::$propertyName`:php:exc:
Reference an exception.
Source CodeLiteral code blocks are created by ending a paragraph with ::. The literal block must be indented, and like all paragraphs
be separated by single lines:
This is a paragraph::while ($i--) {doStuff()}
This is regular text again.
Literal text is not modified or formatted, save that one level of indentation is removed.
Notes and Warnings
There are often times when you want to inform the reader of an important tip, special note or a potential hazard.
Admonitions in sphinx are used for just that. There are fives kinds of admonitions.
• .. tip:: Tips are used to document or re-iterate interesting or important information. The content of thedirective should be written in complete sentences and include all appropriate punctuation.
• .. note:: Notes are used to document an especially important piece of information. The content of the directive should be written in complete sentences and include all appropriate punctuation.
• .. warning:: Warnings are used to document potential stumbling blocks, or information pertaining to security.
The content of the directive should be written in complete sentences and include all appropriate punctuation.
• .. versionadded:: X.Y.Z “Version added” admonitions are used to display notes specific to new features
added at a specific version, X.Y.Z being the version on which the said feature was added.
• .. deprecated:: X.Y.Z As opposed to “version added” admonitions, “deprecated” admonition are used to
notify of a deprecated feature, X.Y.Z being the version on which the said feature was deprecated.
All admonitions are made the same:.. note::Indented and preceded and followed by a blank line. Just like aparagraph.
(continues on next page)

698

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)

This text is not part of the note.

Samples

Tip: This is a helpful tid-bit you probably forgot.

Note: You should pay attention here.

Warning: It could be dangerous.
New in version 2.6.3: This awesome feature was added on version 2.6.3
Deprecated since version 2.6.3: This old feature was deprecated on version 2.6.3

Tickets
Getting feedback and help from the community in the form of tickets is an extremely important part of the CakePHP
development process. All of CakePHP’s tickets are hosted on GitHub136 .

Reporting bugs
Well written bug reports are very helpful. There are a few steps to help create the best bug report possible:• Do search137 for a similar existing ticket, and ensure someone hasn’t already reported your issue, or that it hasn’t
already been fixed in the repository.
• Do include detailed instructions on how to reproduce the bug. This could be in the form of test cases or a codesnippet that demonstrates the issue. Not having a way to reproduce an issue, means it’s less likely to get fixed.
• Do give as many details as possible about your environment: (OS, PHP version, CakePHP version).
• Don’t use the ticket system to ask support questions. Use the Google Group138 , the #cakephp IRC channel orStack Overflow <https://stackoverflow.com/questions/tagged/cakephp> for that.
136 https://github.com/cakephp/cakephp/issues

137 https://github.com/cakephp/cakephp/search?q=it+is+broken&ref=cmdform&type=Issues
138 https://groups.google.com/group/cake-php

Tickets699

CakePHP Cookbook Documentation, Release 2.x

Reporting security issues
If you’ve found a security issue in CakePHP, please use the following procedure instead of the normal bug reporting
system. Instead of using the bug tracker, mailing list or IRC please send an email to security [at] cakephp.org. Emails
sent to this address go to the CakePHP core team on a private mailing list.
For each report, we try to first confirm the vulnerability. Once confirmed, the CakePHP team will take the following
actions:• Acknowledge to the reporter that we’ve received the issue, and are working on a fix. We ask that the reporter
keep the issue confidential until we announce it.
• Get a fix/patch prepared.
• Prepare a post describing the vulnerability, and the possible exploits.
• Release new versions of all affected versions.
• Prominently feature the problem in the release announcement.

Code
Patches and pull requests are a great way to contribute code back to CakePHP. Pull requests can be created in GitHub,
and are prefered over patch files in ticket comments.

Initial setup
Before working on patches for CakePHP, it’s a good idea to get your environment setup. You’ll need the following
software:• Git• PHP 5.3.0 or greater• ext/mcrypt• ext/mbstring• PHPUnit 3.7.0 or greater (3.7.38 recommended)• MySQL, SQLite, or Postgres
Set up your user information with your name/handle and working email address:
git config --global user.name 'Bob Barker'
git config --global user.email 'bob.barker@example.com'
Note: If you are new to Git, we highly recommend you to read the excellent and free ProGit139 book.
Get a clone of the CakePHP source code from GitHub:• If you don’t have a GitHub140 account, create one.
• Fork the CakePHP repository141 by clicking the Fork button.
139 https://git-scm.com/book/
140 https://github.com

141 https://github.com/cakephp/cakephp

700

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x

After your fork is made, clone your fork to your local machine:
git clone git@github.com:YOURNAME/cakephp.git
Add the original CakePHP repository as a remote repository. You’ll use this later to fetch changes from the CakePHP
repository. This will let you stay up to date with CakePHP:
cd cakephp
git remote add upstream git://github.com/cakephp/cakephp.git
Now that you have CakePHP setup you should be able to define a $test database connection, and run all the tests.

Working on a patch
Each time you want to work on a bug, feature or enhancement create a topic branch.
The branch you create should be based on the version that your fix/enhancement is for. For example if you are fixing a
bug in 2.3 you would want to use the 2.3 branch as the base for your branch. If your change is a bug fix for the current
stable release, you should use the master branch. This makes merging your changes in later much simpler:# fixing a bug on 2.3
git fetch upstream
git checkout -b ticket-1234 upstream/2.3

Tip: Use a descriptive name for your branch, referencing the ticket or feature name is a good convention. e.g. ticket1234, feature-awesome
The above will create a local branch based on the upstream (CakePHP) 2.3 branch. Work on your fix, and make as
many commits as you need; but keep in mind the following:• Follow the Coding Standards.
• Add a test case to show the bug is fixed, or that the new feature works.
• Keep your commits logical, and write good clear and concise commit messages.

Submitting a pull request
Once your changes are done and you’re ready for them to be merged into CakePHP, you’ll want to update your branch:
git checkout 2.3
git fetch upstream
git merge upstream/2.3
git checkout <branch_name>git rebase 2.3
This will fetch + merge in any changes that have happened in CakePHP since you started. It will then rebase - or replay
your changes on top of the current code. You might encounter a conflict during the rebase. If the rebase quits early you
can see which files are conflicted/un-merged with git status. Resolve each conflict, and then continue the rebase:
git add <filename> # do this for each conflicted file.
git rebase --continue
Check that all your tests continue to pass. Then push your branch to your fork:
Code

701

CakePHP Cookbook Documentation, Release 2.x

git push origin <branch-name>Once your branch is on GitHub, you can submit a pull request on GitHub.
Choosing where your changes will be merged into
When making pull requests you should make sure you select the correct base branch, as you cannot edit it once the pull
request is created.
• If your change is a bugfix and doesn’t introduce new functionality and only corrects existing behavior that ispresent in the current release. Then choose 2.x as your merge target.
• If your change is a new feature or an addition to the framework, then you should choose the 2.next branch.
• If your change breaks existing functionality, your patch is unlikely to be merged into 2.x. Instead you should
target 4.0
Note: Remember that all code you contribute to CakePHP will be licensed under the MIT License, and the Cake
Software Foundation142 will become the owner of any contributed code. Contributors should follow the CakePHPCommunity Guidelines143 .
All bug fixes merged into a maintenance branch will also be merged into upcoming releases periodically by the coreteam.

Coding Standards
CakePHP developers will use the following coding standards.
It is recommended that others developing CakeIngredients follow the same standards.
You can use the CakePHP Code Sniffer144 to check that your code follows required standards.

Language
All code and comments should be written in English.

Adding New Features
No new features should be added, without having their own tests – which should be passed before committing them tothe repository.
142 https://cakefoundation.org/

143 https://community.cakephp.org/guidelines

144 https://github.com/cakephp/cakephp-codesniffer

702

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x

Indentation
One tab will be used for indentation.
So, indentation should look like this:// base level// level 1// level 2// level 1// base levelOr:$booleanVariable = true;$stringVariable = 'moose';if ($booleanVariable) {echo 'Boolean value is true';
if ($stringVariable === 'moose') {echo 'We have encountered a moose';}}
In cases where you’re using a multi-line function call use the following guidelines:• Opening parenthesis of a multi-line function call must be the last content on the line.
• Only one argument is allowed per line in a multi-line function call.
• Closing parenthesis of a multi-line function call must be on a line by itself.
As an example, instead of using the following formatting:$matches = array_intersect_key($this->_listeners,
array_flip(preg_grep($matchPattern,array_keys($this->_listeners), 0)));Use this instead:$matches = array_intersect_key($this->_listeners,
array_flip(
preg_grep($matchPattern, array_keys($this->_listeners), 0)));

Line Length
It is recommended to keep lines at approximately 100 characters long for better code readability. Lines must not belonger than 120 characters.
In short:• 100 characters is the soft limit.
• 120 characters is the hard limit.

Coding Standards

703

CakePHP Cookbook Documentation, Release 2.x

Control Structures
Control structures are for example “if”, “for”, “foreach”, “while”, “switch” etc. Below, an example with “if”:
if ((expr_1) || (expr_2)) {// action_1;} elseif (!(expr_3) && (expr_4)) {// action_2;} else {// default_action;}• In the control structures there should be 1 (one) space before the first parenthesis and 1 (one) space between thelast parenthesis and the opening bracket.
• Always use curly brackets in control structures, even if they are not needed. They increase the readability of thecode, and they give you fewer logical errors.
• Opening curly brackets should be placed on the same line as the control structure. Closing curly brackets shouldbe placed on new lines, and they should have same indentation level as the control structure. The statement
included in curly brackets should begin on a new line, and code contained within it should gain a new level ofindentation.
• Inline assignments should not be used inside of the control structures.
// wrong = no brackets, badly placed statement
if (expr) statement;// wrong = no brackets
if (expr)
statement;// goodif (expr) {statement;}// wrong = inline assignment
if ($variable = Class::function()) {statement;}// good$variable = Class::function();if ($variable) {statement;}

704

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x

Ternary Operator
Ternary operators are permissible when the entire ternary operation fits on one line. Longer ternaries should be split
into if else statements. Ternary operators should not ever be nested. Optionally parentheses can be used around thecondition check of the ternary for clarity:// Good, simple and readable$variable = isset($options['variable']) ? $options['variable'] : true;// Nested ternaries are bad$variable = isset($options['variable']) ? isset($options['othervar']) ? true : false :␣
˓→false;View Files
In view files (.ctp files) developers should use keyword control structures. Keyword control structures are easier to readin complex view files. Control structures can either be contained in a larger PHP block, or in separate PHP tags:<?php
if ($isAdmin):echo '<p>You are the admin user.</p>';endif;?><p>The following is also acceptable:</p><?php if ($isAdmin): ?><p>You are the admin user.</p><?php endif; ?>We allow PHP closing tags (?>) at the end of .ctp files.

Comparison
Always try to be as strict as possible. If a none strict test is deliberate it might be wise to comment it as such to avoid
confusing it for a mistake.
For testing if a variable is null, it is recommended to use a strict check:if ($value === null) {// ...
}The value to check against should be placed on the right side:// not recommended
if (null === $this->foo()) {// ...
}// recommended
if ($this->foo() === null) {// ...
}

Coding Standards

705CakePHP Cookbook Documentation, Release 2.x

Function Calls
Functions should be called without space between function’s name and starting bracket. There should be one space
between every parameter of a function call:$var = foo($bar, $bar2, $bar3);
As you can see above there should be one space on both sides of equals sign (=).

Method Definition
Example of a method definition:
public function someFunction($arg1, $arg2 = '') {if (expr) {statement;}
return $var;}
Parameters with a default value, should be placed last in function definition. Try to make your functions return something, at least true or false, so it can be determined whether the function call was successful:
public function connection($dns, $persistent = false) {if (is_array($dns)) {$dnsInfo = $dns;} else {$dnsInfo = BD::parseDNS($dns);}
if (!($dnsInfo) || !($dnsInfo['phpType'])) {return $this->addError();}return true;}
There are spaces on both side of the equals sign.
Typehinting
Arguments that expect objects or arrays can be typehinted:/*** Some method description.
** @param Model $Model The model to use.
* @param array $array Some array value.
* @param bool $boolean Some boolean value.
*/
public function foo(Model $Model, array $array, $boolean) {}706

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x

Here $Model must be an instance of Model and $array must be an array.
Note that if you want to allow $array to be also an instance of ArrayObject you should not typehint as array accepts
only the primitive type:/*** Some method description.
** @param array|ArrayObject $array Some array value.
*/
public function foo($array) {}Method Chaining
Method chaining should have multiple methods spread across separate lines, and indented with one tab:$email->from('foo@example.com')->to('bar@example.com')->subject('A great message')->send();DocBlocks
All comment blocks, with the exception of the first block in a file, should always be preceded by a newline.
File Header DocBlock
All PHP files should contain a file header DocBlock, which should look like this:
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)** Licensed under The MIT License* For full copyright and license information, please see the LICENSE.txt* Redistributions of files must retain the above copyright notice.
** @copyright
Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.
˓→org)* @link
https://cakephp.org CakePHP(tm) Project* @since
X.Y.Z* @license
https://www.opensource.org/licenses/mit-license.php MIT License*/
The included phpDocumentor145 tags are:• @copyright146
145 https://phpdoc.org

146 https://phpdoc.org/docs/latest/references/phpdoc/tags/copyright.html

Coding Standards

707CakePHP Cookbook Documentation, Release 2.x• @link147• @since148• @license149
Class DocBlocks
Class DocBlocks should look like this:/*** Short description of the class.
** Long description of class.
* Can use multiple lines.
** @deprecated 3.0.0 Deprecated in 2.6.0. Will be removed in 3.0.0. Use Bar instead.
* @see Bar* @link https://book.cakephp.org/2.0/en/foo.html*/
class Foo {}Class DocBlocks may contain the following phpDocumentor150 tags:• @deprecated151 Using the @version <vector> <description> format, where version and description
are mandatory.
• @internal152• @link153• @property154• @see155• @since156• @uses157
147 https://phpdoc.org/docs/latest/references/phpdoc/tags/link.html

148 https://phpdoc.org/docs/latest/references/phpdoc/tags/since.html

149 https://phpdoc.org/docs/latest/references/phpdoc/tags/license.html
150 https://phpdoc.org

151 https://phpdoc.org/docs/latest/references/phpdoc/tags/deprecated.html
152 https://phpdoc.org/docs/latest/references/phpdoc/tags/internal.html
153 https://phpdoc.org/docs/latest/references/phpdoc/tags/link.html

154 https://phpdoc.org/docs/latest/references/phpdoc/tags/property.html
155 https://phpdoc.org/docs/latest/references/phpdoc/tags/see.html

156 https://phpdoc.org/docs/latest/references/phpdoc/tags/since.html
157 https://phpdoc.org/docs/latest/references/phpdoc/tags/uses.html

708

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x

Property DocBlocks
Property DocBlocks should look like this:/*** @var string|null Description of property.
** @deprecated 3.0.0 Deprecated as of 2.5.0. Will be removed in 3.0.0. Use $_bla instead.
* @see Bar::$_bla* @link https://book.cakephp.org/2.0/en/foo.html#properties*/
protected $_bar = null;
Property DocBlocks may contain the following phpDocumentor158 tags:• @deprecated159 Using the @version <vector> <description> format, where version and description
are mandatory.
• @internal160• @link161• @see162• @since163• @var164
Method/Function DocBlocks
Method and functions DocBlocks should look like this:/*** Short description of the method.
** Long description of method.
* Can use multiple lines.
** @param string $param2 first parameter.
* @param array|null $param2 Second parameter.
* @return array An array of cakes.
* @throws Exception If something goes wrong.
** @link https://book.cakephp.org/2.0/en/foo.html#bar* @deprecated 3.0.0 Deprecated as of 2.5.0. Will be removed in 3.0.0. Use Bar::baz␣˓→instead.
* @see Bar::baz*/public function bar($param1, $param2 = null) {}158 https://phpdoc.org

159 https://phpdoc.org/docs/latest/references/phpdoc/tags/deprecated.html
160 https://phpdoc.org/docs/latest/references/phpdoc/tags/internal.html
161 https://phpdoc.org/docs/latest/references/phpdoc/tags/link.html
162 https://phpdoc.org/docs/latest/references/phpdoc/tags/see.html

163 https://phpdoc.org/docs/latest/references/phpdoc/tags/since.html
164 https://phpdoc.org/docs/latest/references/phpdoc/tags/var.html

Coding Standards709

CakePHP Cookbook Documentation, Release 2.x

Method and function DocBLocks may contain the following phpDocumentor165 tags:• @deprecated166 Using the @version <vector> <description> format, where version and description
are mandatory.
• @internal167• @link168• @param169• @return170• @throws171• @see172• @since173• @uses174
Variable Types
Variable types for use in DocBlocks:
Type
Description
mixed
A variable with undefined (or multiple) type.
int
Integer type variable (whole number).
float
Float type (point number).
bool
Logical type (true or false).
stringString type (any value in “ “ or ‘ ‘).
null
Null type. Usually used in conjunction with another type.
array
Array type.
objectObject type. A specific class name should be used if possible.
165 https://phpdoc.org

166 https://phpdoc.org/docs/latest/references/phpdoc/tags/deprecated.html
167 https://phpdoc.org/docs/latest/references/phpdoc/tags/internal.html
168 https://phpdoc.org/docs/latest/references/phpdoc/tags/link.html

169 https://phpdoc.org/docs/latest/references/phpdoc/tags/param.html
170 https://phpdoc.org/docs/latest/references/phpdoc/tags/return.html

171 https://phpdoc.org/docs/latest/references/phpdoc/tags/throws.html
172 https://phpdoc.org/docs/latest/references/phpdoc/tags/see.html

173 https://phpdoc.org/docs/latest/references/phpdoc/tags/since.html
174 https://phpdoc.org/docs/latest/references/phpdoc/tags/uses.html

710

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x

resource
Resource type (returned by for example mysql_connect()). Remember that when you specify the type as mixed,
you should indicate whether it is unknown, or what the possible types are.
callable
Callable function.
You can also combine types using the pipe char:
int|bool
For more than two types it is usually best to just use mixed.
When returning the object itself, e.g. for chaining, one should use $this instead:/*** Foo function.
** @return $this*/public function foo() {return $this;}Including Files
include, require, include_once and require_once do not have parentheses:// wrong = parentheses
require_once('ClassFileName.php');
require_once ($class);// good = no parentheses
require_once 'ClassFileName.php';
require_once $class;
When including files with classes or libraries, use only and always the require_once175 function.

PHP Tags
Always use long tags (<?php ?>) Instead of short tags (<? ?>).

Naming Convention
FunctionsWrite all functions in camelBack:function longFunctionName() {}175 https://www.php.net/require_once

Coding Standards

711CakePHP Cookbook Documentation, Release 2.x

ClassesClass names should be written in CamelCase, for example:
class ExampleClass {}Variables
Variable names should be as descriptive as possible, but also as short as possible. All variables should start with alowercase letter, and should be written in camelBack in case of multiple words. Variables referencing objects shouldin some way associate to the class the variable is an object of. Example:$user = 'John';$users = array('John', 'Hans', 'Arne');$dispatcher = new Dispatcher();Member Visibility
Use PHP5’s private and protected keywords for methods and variables. Additionally, protected method or variablenames start with a single underscore (_). Example:
class A {protected $_iAmAProtectedVariable;
protected function _iAmAProtectedMethod() {/* ... */}}Private methods or variable names start with double underscore (__). Example:
class A {private $__iAmAPrivateVariable;
private function __iAmAPrivateMethod() {/* ... */}}Try to avoid private methods or variables, though, in favor of protected ones. The latter can be accessed or modified bysubclasses, whereas private ones prevent extension or re-use. Private visibility also makes testing much more difficult.

712

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x

Example Addresses
For all example URL and mail addresses use “example.com”, “example.org” and “example.net”, for example:• Email: someone@example.com• WWW: http://www.example.com• FTP: ftp://ftp.example.com
The “example.com” domain name has been reserved for this (see RFC 2606176 ) and is recommended for use in documentation or as examples.
Files
File names which do not contain classes should be lowercased and underscored, for example:
long_file_name.php

Casting
For casting we use:
Type
Description(bool)
Cast to boolean.
(int)Cast to integer.
(float)Cast to float.
(string)
Cast to string.
(array)Cast to array.
(object)
Cast to object.
Please use (int)$var instead of intval($var) and (float)$var instead of floatval($var) when applicable.
Constants
Constants should be defined in capital letters:
define('CONSTANT', 1);
If a constant name consists of multiple words, they should be separated by an underscore character, for example:
define('LONG_NAMED_CONSTANT', 2);
176 https://datatracker.ietf.org/doc/html/rfc2606.html

Coding Standards

713CakePHP Cookbook Documentation, Release 2.x

Backwards Compatibility Guide
Ensuring that you can upgrade your applications easily and smoothly is important to us. That’s why we only breakcompatibility at major release milestones. You might be familiar with semantic versioning177 , which is the general
guideline we use on all CakePHP projects. In short, semantic versioning means that only major releases (such as 2.0,
3.0, 4.0) can break backwards compatibility. Minor releases (such as 2.1, 3.1, 3.2) may introduce new features, but
are not allowed to break compatibility. Bug fix releases (such as 2.1.2, 3.0.1) do not add new features, but fix bugs orenhance performance only.
Note: CakePHP started following semantic versioning in 2.0.0. These rules do not apply to 1.x.
To clarify what changes you can expect in each release tier we have more detailed information for developers using
CakePHP, and for developers working on CakePHP that helps set expectations of what can be done in minor releases.
Major releases can have as many breaking changes as required.

Migration Guides
For each major and minor release, the CakePHP team will provide a migration guide. These guides explain the newfeatures and any breaking changes that are in each release. They can be found in the Appendices section of the cookbook.

Using CakePHP
If you are building your application with CakePHP, the following guidelines explain the stability you can expect.
Interfaces
Outside of major releases, interfaces provided by CakePHP will not have any existing methods changed and new
methods will not be added to any existing interfaces.
ClassesClasses provided by CakePHP can be constructed and have their public methods and properties used by application
code and outside of major releases backwards compatibility is ensured.
Note: Some classes in CakePHP are marked with the @internal API doc tag. These classes are not stable and do
not have any backwards compatibility promises.
In minor releases (3.x.0), new methods may be added to classes, and existing methods may have new arguments added.
Any new arguments will have default values, but if you’ve overidden methods with a differing signature you may see
errors. Methods that have new arguments added will be documented in the migration guide for that release.
The following table outlines several use cases and what compatibility you can expect from CakePHP:
177 https://semver.org/

714

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x

If you. . .

Backwards compatibility?

Typehint against the classCreate a new instance
Extend the class
Access a public property
Call a public method
Extend a class and. . .
Call a protected method
Override a protected property
Override a protected method
Access a protected property
Call a public method
Override a public method
Override a public property
Add a public property
Add a public method
Add an argument to an overridden method
Add a default argument to an existing method

Yes
Yes
Yes
Yes
Yes
No1
NoPage 715, 1
No1
No1
Yes
Yes1
Yes
No
No
No1
Yes

Working on CakePHP
If you are helping make CakePHP even better please keep the following guidelines in mind when adding/changing
functionality:
In a minor release you can:
1 Your code may be broken by minor releases. Check the migration guide for details.

Backwards Compatibility Guide

715

CakePHP Cookbook Documentation, Release 2.x

In a minor release can you. . .
Classes
Remove a class
Remove an interface
Remove a trait
Make final
Make abstract
Change name
Properties
Add a public property
Remove a public property
Add a protected property
Remove a protected property
Methods
Add a public method
Remove a public method
Add a protected method
Move member to parent class
Remove a protected method
Reduce visibility
Change method nameAdd default value to existing argument
Add argument with default value
Add required argument

No
No
No
No
No
Yes2
Yes
No
Yes
Yes3
Yes
No
Yes
Yes
YesPage 716, 3
No
Yes2
No
Yes
No

CakePHP Development Process
Here we attempt to explain the process we use when developing the CakePHP framework. We rely heavily on community interaction through tickets and IRC chat. IRC is the best place to find members of the development team178 anddiscuss ideas, the latest code, and make general comments. If something more formal needs to be proposed or there isa problem with a release, the ticket system is the best place to share your thoughts.
We currently maintain 4 versions of CakePHP.
• stable : Tagged releases intended for production where stability is more important than features. Issues filed
against these releases will be fixed in the related branch, and be part of the next release.
• maintenance branch : Development branches become maintenance branches once a stable release point has
been reached. Maintenance branches are where all bugfixes are committed before making their way into a stablerelease. Maintenance branches have the same name as the major version they are for example 1.2. If you areusing a stable release and need fixes that haven’t made their way into a stable release check here.
• development branches : Development branches contain leading edge fixes and features. They are named after
the version number they are for example 1.3. Once development branches have reached a stable release point
they become maintenance branches, and no further new features are introduced unless absolutely necessary.
• feature branches : Feature branches contain unfinished or possibly unstable features and are recommended
only for power users interested in the most advanced feature set and willing to contribute back to the community.
2 You can change a class/method name as long as the old name remains available. This is generally avoided unless renaming has significant
benefit.
3 Avoid whenever possible. Any removals need to be documented in the migration guide.
178 https://github.com/cakephp?tab=members

716

Chapter 13. Contributing

CakePHP Cookbook Documentation, Release 2.x

Feature branches are named with the following convention version-feature. An example would be 1.3-router
Which would contain new features for the Router for 1.3.
Hopefully this will help you understand what version is right for you. Once you pick your version you may feel compelled to contribute a bug report or make general comments on the code.
• If you are using a stable version or maintenance branch, please submit tickets or discuss with us on IRC.
• If you are using the development branch or feature branch, the first place to go is IRC. If you have a commentand cannot reach us in IRC after a day or two, please submit a ticket.
If you find an issue, the best answer is to write a test. The best advice we can offer in writing tests is to look at the ones
included in the core.
As always, if you have any questions or comments, visit us at #cakephp on irc.freenode.net.

CakePHP Development Process717

CakePHP Cookbook Documentation, Release 2.x

718

Chapter 13. Contributing

CHAPTER 14

Appendices

Appendices contain information regarding the new features introduced in 2.x, and the migration path from 1.3 to 2.0.

2.10 Migration Guide
2.10 Migration Guide
CakePHP 2.10 is a fully API compatible upgrade from 2.9. This page outlines the changes and improvements made in
2.10.
Console• Using undefined CLI options will now result in an error. This behavior has been backported from 3.x
Core• The CONFIG constant was added. This constant defaults to app/Config, and is intended to increase forwards
compatibility with 3.x

719

CakePHP Cookbook Documentation, Release 2.x

Model• New internal data types were added for smallinteger and tinyinteger. Existing SMALLINT and TINYINT
columns will now be reflected as these new internal data types. TINYINT(1) columns will continue to be treatedas boolean columns in MySQL.
• Model::find() now supports having and lock options that enable you to add HAVING and FOR UPDATE
locking clauses to your find operations.
• TranslateBehavior now supports loading translations with LEFT JOIN. Use the joinType option to use thisfeature.
Components• SecurityComponent now emits more verbose error messages when form tampering or CSRF protection fails
in debug mode. This feature was backported from 3.x• SecurityComponent will blackhole post requests that have no request data now. This change helps protect
actions that create records using database defaults alone.
• FlashComponent now stacks messages of the same type. This is a feature backport from 3.x. To disable thisbehavior, add 'clear' => true to the configuration for FlashComponent.
• PaginatorComponent now supports multiple paginators through the queryScope option. Using this option
when paginating data will force PaginatorComponent to read from scoped query parameters instead of the root
query string data.
Helpers• HtmlHelper::image() now supports the base64 option. This option will read local image files and createbase64 data URIs.
• HtmlHelper::addCrumb() had the prepend option added. This lets you prepend a breadcrumb instead of
appending to the list.
• FormHelper creates ‘numeric’ inputs for smallinteger and tinyinteger types.
Routing• Router::reverseToArray() was added.

2.9 Migration Guide
2.9 Migration Guide
CakePHP 2.9 is a fully API compatible upgrade from 2.8. This page outlines the changes and improvements made in
2.9.

720

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

PHP7 Compatibility
CakePHP 2.9 is compatible with, and tested against PHP7.
Deprecations• The Object class has been deprecated and renamed to CakeObject due to object becoming a reserved keyword
in one of the next PHP7 minors (see [RFC](https://wiki.php.net/rfc/reserve_even_more_types_in_php_7)).
New Features• DboSource::flushQueryCache() was added to allow more fine-grained control of query result caching whenenabled.
• The log messages created by ErrorHandler can now be more easily customized in subclasses.
• Additional mime-types for ‘jsonapi’, and ‘psd’ were added.
• Time & Datetime inputs no longer set a maxlength attribute when rendered as ‘text’ input types.
• AuthComponent::user() now makes the user data available when using stateless authentication adapters.

2.8 Migration Guide
2.8 Migration Guide
CakePHP 2.8 is a fully API compatible upgrade from 2.7. This page outlines the changes and improvements made in
2.8.
PHP7 Compatibility
CakePHP 2.8 is compatible with, and tested against PHP7.
Deprecations• The action option in FormHelper::create() has been deprecated. This is a backport from 3.x. Note that
this now makes the action key of an array URL consistently respected for the generation of the DOM ID. If you
used the deprecated key you want to compare the generated ID for the form before and after.
Error Handling• When handling fatal errors, CakePHP will now adjust the memory limit by 4MB to ensure that the error canbe logged correctly. You can disable this behavior by setting Error.extraFatalErrorMemory to 0 in yourConfig/core.php.

2.8 Migration Guide

721

CakePHP Cookbook Documentation, Release 2.x

Cache• Cache::add() has been added. This method lets you add data to a cache if the key did not already exist. Thismethod will work atomically in Memcached, Memcache, APC and Redis. Other cache backends will do nonatomic operations.
CakeTime• CakeTime::listTimezones() has been changed to accept array in the last argument. Valid values for the$options argument are: group, abbr, before, and after.
Shell Helpers Added
Console applications can now create helper classes that encapsulate re-usable blocks of output logic. See the Shell
Helpers section for more information.
I18nShell• A new option no-locations has been added. When enabled, this option will disable the generation of locationreferences in your POT files.
Hash• Hash::sort() now supports case-insensitive sorting via the ignoreCase option.
Model• Magic finders now support custom finder types. For example if your model implements a find('published')
finder, you can now use findPublishedBy and findPublishedByAuthorId functions through the magic
method interface.
• Find conditions can now use IN and NOT IN operator in conditions. This allows find expressions to be more
forwards compatible with 3.x.
Validation• Validation::uploadedFile() was backported from 3.x.
CakeSession• The Session.cacheLimiter configuration option was added. This option lets you define the cache control
headers used for the session cookie. The default is must-revalidate.

722

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

View
FormHelper'url' => false is now supported for FormHelper::create() to allow form tags to be created without HTMLaction attribute. This is a backport from 3.x.

2.7 Migration Guide
2.7 Migration Guide
CakePHP 2.7 is a fully API compatible upgrade from 2.6. This page outlines the changes and improvements made in
2.7.
Requirements
The PHP version requirement for CakePHP 2.7 has been bumped up to PHP 5.3.0.
Console• Plugin shells that share a name with their plugin can now be called without the plugin prefix. For example
Console/cake MyPlugin.my_plugin can now be called with Console/cake my_plugin.
• Shell::param() was backported from 3.0 into 2.7. This method provides a notice error free way to read CLI
options.
Core
Configure• Configure::consume() has been added to read and delete from Configure in a single step.
Datasource• SQL datasources will now cast '' and null into '' when columns are not nullable and rows are being createdor updated.
CakeSession• CakeSession::consume() has been added to read and delete from session in a single step.
• Argument $renew has been added to CakeSession::clear() to allow emptying the session without forcing a
new id and renewing the session. It defaults to true.

2.7 Migration Guide723

CakePHP Cookbook Documentation, Release 2.x

Model
TreeBehavior• New setting level is now available. You can use it to specify field name in which the depth of tree nodes will be
stored.
• New method TreeBehavior::getLevel() has been added which fetches depth of a node.
• The formatting of TreeBehavior::generateTreeList() has been extracted into an own method
TreeBehavior::formatTreeList().
Network
CakeEmail• CakeEmail will now use the ‘default’ config set when creating instances that do not specify a configuration set
to use. For example $email = new CakeEmail(); will now use the ‘default’ config set.
Utility
CakeText
The class String has been renamed to CakeText. This resolves some conflicts around HHVM compatibility as wellas possibly PHP7+. There is a String class provided as well for compatibility reasons.
Validation• Validation::notEmpty() has been renamed to Validation::notBlank(). This aims to avoid confusion
around the PHP notEmpty() function and that the validation rule accepts 0 as valid input.
Controller
SessionComponent• SessionComponent::consume() has been added to read and delete from session in a single step.
• SessionComponent::setFlash() has been deprecated. You should use FlashComponent instead.
RequestHandlerComponent• The text/plain Accept header is no longer automatically mapped to the csv response type. This is a backportfrom 3.0724

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

View
SessionHelper• SessionHelper::consume() has been added to read and delete from session in a single step.
• SessionHelper::flash() has been deprecated. You should use FlashHelper instead.
TestSuite
ControllerTestCase• ControllerTestCase::testAction() now supports an array as URL.

2.6 Migration Guide
2.6 Migration Guide
CakePHP 2.6 is a fully API compatible upgrade from 2.5. This page outlines the changes and improvements made in
2.6.
Basics.php• stackTrace() has been added as a convenience wrapper function for Debugger::trace(). It directly echos
just as debug() does. But only if debug level is on.
• New i18n functions have been added. The new functions allow you to include message context which allows
you disambiguate possibly confusing message strings. For example ‘read’ can mean multiple things in English
depending on the context. The new __x, __xn, __dx, __dxn, __dxc, __dxcn, and __xc functions provide accessto the new features.
Cache
RedisEngine• The RedisEngine now has a default prefix of Inflector::slug(APP_DIR).
ConsoleConsoleOptionParser• ConsoleOptionParser::removeSubcommand() was added.

2.6 Migration Guide

725

CakePHP Cookbook Documentation, Release 2.x

Shell• overwrite() has been added to allow generating progress bars or to avoid outputting too many lines by replacing
text that has been already outputted to the screen.
Controller
AuthComponent• AuthComponent had the userFields option added.
• AuthComponent now triggers an Auth.afterIdentify event after a user has been identified and logged in.
The event will contain the logged in user as data.
Behavior
AclBehavior• Model::parentNode() now$model->parentNode($type).

gets

the

type

(Aro,

Aco)

passedasfirstargument:

Datasource
Mysql• The RLIKE wildcard operator has been added to allow regular expression pattern lookups this way.
• Schema migrations with MySQL now support an after key when adding a column. This key allows you to
specify which column the new one should be added after.
Model
Model
• Model::save() had the atomic option back-ported from 3.0.
• Model::afterFind() now always uses a consistent format for afterFind(). When $primary is false, the results
will always be located under $data[0]['ModelName']. You can set the useConsistentAfterFind property
to false on your models to restore the original behavior.
Network
CakeRequest• CakeRequest::param() can now read values using Hash path syntax like data().
• CakeRequest:setInput() was added.

726

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

HttpSocket• HttpSocket::head() was added.
• You can now use the protocol option to override the specific protocol to use when making a request.
I18n• Configure value I18n.preferApp can now be used to control the order of translations. If set to true it will prefer
the app translations over any plugins’ ones.
Utility
CakeTime• CakeTime::timeAgoInWords() now supports strftime() compatible absolute date formats. This helps
make localizing formatted times easier.
Hash• Hash::get() now raises an exception when the path argument is invalid.
• Hash::nest() now raises an exception when the nesting operation results in no data.
Validation• Validation::between has been deprecated, you should use Validation::lengthBetween instead.
• Validation::ssn has been deprecated and can be provided as standalone/plugin solution.
View
JsonView• JsonView now supports the _jsonOptions view variable. This allows you to configure the bit-mask options
used when generating JSON.
XmlView• XmlView now supports the _xmlOptions view variable. This allows you to configure the options used whengenerating XML.

2.6 Migration Guide

727

CakePHP Cookbook Documentation, Release 2.x

Helper
HtmlHelper• HtmlHelper::css() had the once option added.
It works the same as the once option for
HtmlHelper::script(). The default value is false to maintain backwards compatibility.
• The $confirmMessage argument of HtmlHelper::link() has been deprecated. You should instead use key
confirm in $options to specify the message.
FormHelper• The $confirmMessage argument of FormHelper::postLink() has been deprecated. You should instead use
key confirm in $options to specify the message.
• The maxlength attribute will now also be applied to textareas, when the corresponding DB field is of typevarchar, as per HTML specs.
PaginatorHelper• PaginatorHelper::meta() has been added to output the meta-links (rel prev/next) for a paginated result set.

2.5 Migration Guide
2.5 Migration Guide
CakePHP 2.5 is a fully API compatible upgrade from 2.4. This page outlines the changes and improvements made in
2.5.
Cache
• A new adapter has been added for Memcached. This new adapter uses ext/memcached instead of ext/memcache.
It supports improved performance and shared persistent connections.
• The Memcache adapter is now deprecated in favor of Memcached.
• Cache::remember() was added.
• Cache::config() now accepts database key when used with RedisEngine in order to use non-default
database number.
ConsoleSchemaShell• The create and update subcommands now have a yes option. The yes option allows you to skip the various
interactive questions forcing a yes reply.

728

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

CompletionShell• The CompletionShell was added. It aims to assist in the creation of autocompletion libraries for shell environments like bash, or zsh. No shell scripts are included in CakePHP, but the underlying tools are now available.
Controller
AuthComponent• loggedIn() is now deprecated and will be removed in 3.0.
• When using ajaxLogin, AuthComponent will now return a 403 status code instead of a 200 when the user is
un-authenticated.
CookieComponent• CookieComponent can use the new AES-256 encryption offered by Security. You can enable this by calling
CookieComponent::type() with ‘aes’.
RequestHandlerComponent• RequestHandlerComponent::renderAs() no longer sets Controller::$ext. It caused problems whenusing a non default extension for views.
AclComponent• ACL node lookup failures are now logged directly. The call to trigger_error() has been removed.
Scaffold• Dynamic Scaffold is now deprecated and will be removed in 3.0.
CoreApp
• App::pluginPath() has been deprecated. CakePlugin::path() should be used instead.

2.5 Migration Guide

729

CakePHP Cookbook Documentation, Release 2.x

CakePlugin• CakePlugin::loadAll() now merges the defaults and plugin specific options as intuitively expected. See thetest cases for details.
Event
EventManager
Events bound to the global manager are now fired in priority order with events bound to a local manager. This can cause
listeners to be fired in a different order than they were in previous releases. Instead of all global listeners being triggered,and then instance listeners being fired afterwards, the two sets of listeners are combined into one list of listeners basedon their priorities and then fired as one set. Global listeners of a given priority are still fired before instance listeners.
I18n• The I18n class has several new constants. These constants allow you to replace hardcoded integers with readable
values. e.g. I18n::LC_MESSAGES.
Model• Unsigned integers are now supported by datasources that provide them (MySQL). You can set the unsigned
option to true in your schema/fixture files to start using this feature.
• Joins included in queries are now added after joins from associations are added. This makes it easier to jointables that depend on generated associations.
Network
CakeEmail• Email addresses in CakeEmail are now validated with filter_var by default. This relaxes the email address
rules allowing internal email addresses like root@localhost for example.
• You can now specify layout key in email config array without having to specify template key.
CakeRequest• CakeRequest::addDetector() now supports options which accepts an array of valid options when creating
param based detectors.
• CakeRequest::onlyAllow() has been deprecated.
As replacement a new method named
CakeRequest::allowMethod() has been added with identical functionality. The new method name ismore intuitive and better conveys what the method does.

730

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

CakeSession• Sessions will not be started if they are known to be empty. If the session cookie cannot be found, a session will
not be started until a write operation is done.
Routing
Router• Router::mapResources() accepts connectOptions key in the $options argument. See Custom REST Routing for more details.
Utility
Debugger• Debugger::dump() and Debugger::log() now support a $depth parameter. This new parameter makes it
easy to output more deeply nested object structures.
Hash• Hash::insert() and Hash::remove() now support matcher expressions in their path selectors.
File• File::replaceText() was added. This method allows you to easily replace text in a file using str_replace.
Folder• Folder::addPathElement() now accepts an array for the $element parameter.
Security• Security::encrypt() and Security::decrypt() were added. These methods expose a very simple API
to access AES-256 symmetric encryption. They should be used in favour of the cipher() and rijndael()methods.
Validation• The third param for Validation::inList() and Validation::multiple() has been modified from $strict
to $caseInsensitive. $strict has been dropped as it was working incorrectly and could easily backfire. You can
now set this param to true for case insensitive comparison. The default is false and will compare the value andlist case sensitive as before.
• $mimeTypes parameter of Validation::mimeType() can also be a regex string. Also now when $mimeTypes
is an array it’s values are lowercased.

2.5 Migration Guide

731

CakePHP Cookbook Documentation, Release 2.x

Logging
FileLog• CakeLog does not auto-configure itself anymore. As a result log files will not be auto-created anymore if nostream is listening. Please make sure you got at least one default engine set up if you want to listen to all types
and levels.
Error
ExceptionRenderer
The ExceptionRenderer now populates the error templates with “code”, “message” and “url” variables. “name” hasbeen deprecated but is still available. This unifies the variables across all error templates.
Testing• Fixture files can now be placed in sub-directories. You can use fixtures in subdirectories by including
the directory name after the .. For example, app.my_dir/article will load App/Test/Fixture/my_dir/
ArticleFixture. It should be noted that the fixture directory will not be inflected or modified in any way.
• Fixtures can now set $canUseMemory to false to disable the memory storage engine being used in MySQL.
View
View
• $title_for_layout is deprecated.
'your-page-title'); instead.

Use $this->fetch('title'); and $this->assign('title',• View::get() now accepts a second argument to provide a default value.
FormHelper• FormHelper will now generate file inputs for binary field types now.
• FormHelper::end() had a second parameter added. This parameter lets you pass additional properties to thefields used for securing forms in conjunction with SecurityComponent.
• FormHelper::end() and FormHelper::secure() allow you to pass additional options that are turned into
attributes on the generated hidden inputs. This is useful when you want to use the HTML5 form attribute.
• FormHelper::postLink() now allows you to buffer the generated form tag instead of returning it with thelink. This helps avoiding nested form tags.

732

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

PaginationHelper• PaginatorHelper::sort() now has a lock option to create pagination sort links with the default direction
only.
ScaffoldView• Dynamic Scaffold is now deprecated and will be removed in 3.0.

2.4 Migration Guide
2.4 Migration Guide
CakePHP 2.4 is a fully API compatible upgrade from 2.3. This page outlines the changes and improvements made in
2.4.
Console• Logged notice messages will now be colourized in terminals that support colours.
• ConsoleShell is now deprecated.
SchemaShell• cake schema generate now supports the --exclude parameter.
• The constant CAKEPHP_SHELL is now deprecated and will be removed in CakePHP 3.0.
BakeShell• cake bake model now supports baking $behaviors. Finding lft, rght and parent_id fields in your table it will
add the Tree behavior, for example. You can also extend the ModelTask to support your own behaviors to berecognized.
• cake bake for views, models, controllers, tests and fixtures now supports a -f or --force parameter to force
overwriting of files.
• Tasks in core can now be aliased in the same way you would Helpers, Components and Behaviors
FixtureTask• cake bake fixture now supports a --schema parameter to allow baking all fixtures with noninteractive “all”
while using schema import.

2.4 Migration Guide733

CakePHP Cookbook Documentation, Release 2.x

CoreConstants• Constants IMAGES_URL, JS_URL, CSS_URL have been deprecated and replaced with config variables App.
imageBaseUrl, App.jsBaseUrl, App.cssBaseUrl respectively.
• Constants IMAGES, JS, CSS have been deprecated.
Object• Object::log() had the $scope parameter added.
Components
AuthComponent• AuthComponent now supports proper stateless mode when using ‘Basic’ or ‘Digest’ authenticators. Startingof session can be prevented by setting AuthComponent::$sessionKey to false. Also now when using only
‘Basic’ or ‘Digest’ you are no longer redirected to login page. For more info check the AuthComponent page.
• Property AuthComponent::$authError can be set to boolean false to suppress flash message from being
displayed.
PasswordHasher• Authenticating objects now use new password hasher objects for password hash generation and checking. See
Hashing passwords for more info.
DbAcl• DbAcl now uses INNER joins instead of LEFT joins. This improves performance for some database vendors.
Model
Models• Model::save(),
Model::saveField(),
Model::saveAll(),
Model::saveAssociated(),Model::saveMany() now take a new counterCache option. You can set it to false to avoid updating
counter cache values for the particular save operation.
• Model::clear() was added.

734

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Datasource• The Mysql, Postgres, and Sqlserver datasources now support a ‘settings’ array in the connection definition. This
key => value pair will be issued as SET commands when the connection is created.
• The MySQL driver now supports SSL options.
View
JsonView• JSONP support has been added to JsonView.
• The _serialize key now supports renaming serialized variables.
• When debug > 0 JSON will be pretty printed.
XmlView• The _serialize key now supports renaming serialized variables.
• When debug > 0 XML will be pretty printed.
HtmlHelper• The API for HtmlHelper::css() has been been simplified. You can now provide an array of options as thesecond argument. When you do, the rel attribute defaults to ‘stylesheet’.
• New option escapeTitle added to HtmlHelper::link() to control escaping of only link title and not attributes.
TextHelper• TextHelper::autoParagraph() has been added. It allows to automatically convert text into HTML paragraphs.
PaginatorHelper• PaginatorHelper::param() has been added.
• The first page no longer contains /page:1 or ?page=1 in the URL. This helps prevent duplicate content issues
where you would need to use canonical or noindex otherwise.

2.4 Migration Guide

735

CakePHP Cookbook Documentation, Release 2.x

FormHelper• The round option was added to FormHelper::dateTime(). Can be set to up or down to force rounding ineither direction. Defaults to null which rounds half up according to interval.
Network
CakeRequest• CakeRequest::param() has been added.
• CakeRequest::is() has been modified to support an array of types and will return true if the request matches
any type.
• CakeRequest::isAll() has been added to check that a request matches all the given types.
CakeResponse• CakeResponse::location() has been added to get or set the redirect location header.
CakeEmail• Logged email messages now have the scope of email by default. If you are not seeing email contents in yourlogs, be sure to add the email scope to your logging configuration.
• CakeEmail::emailPattern() was added. This method can be used to relax email validation rules. This isuseful when dealing with certain Japanese hosts that allow non-compliant addresses to be used.
• CakeEmail::attachments() now allows you to provide the file contents directly using the data key.
• Configuration data is now correctly merged with transport classes.
HttpSocket• HttpSocket::patch() has been added.
I18n
L10n
• ell is now the default locale for Greek as specified by ISO 639-3 and gre its alias. The locale folders have tobe adjusted accordingly (from /Locale/gre/ to /Locale/ell/ ).
• fas is now the default locale for Farsi as specified by ISO 639-3 and per its alias. The locale folders have to be
adjusted accordingly (from /Locale/per/ to /Locale/fas/ ).
• sme is now the default locale for Sami as specified by ISO 639-3 and smi its alias. The locale folders have to be
adjusted accordingly (from /Locale/smi/ to /Locale/sme/ ).
• mkd replaces mk as default locale for Macedonian as specified by ISO 639-3. The corresponding locale folders
have to be adjusted, as well.

736

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x• Catalog code in has been dropped in favor of id (Indonesian), e has been dropped in favor of el (Greek), n has
been dropped in favor of nl (Dutch), p has been dropped in favor of pl (Polish), sz has been dropped in favorof se (Sami).
• Kazakh has been added with kaz as locale and kk as catalog code.
• Kalaallisut has been added with kal as locale and kl as catalog code.
• The constant DEFAULT_LANGUAGE has been deprecated in favor of Configure value Config.language.
Logging• Log engines do not need the suffix Log anymore in their setup configuration. So for the FileLog engine it suffices
to define 'engine' => 'File' now. This unifies the way engines are named in configuration (see Cache
engines for example). Note: If you have a Log engine like DatabaseLogger that does not follow the convention
to use a suffix Log for your class name you have to adjust your class name to DatabaseLog. You should also
avoid class names like SomeLogLog which include the suffix twice at the end.
FileLog• Two new config options size and rotate have been added for FileLog engine.
• In debug mode missing directories will now be automatically created to avoid unnecessary errors thrown.
SyslogLog• The new logging engine SyslogLog was added to stream messages to syslog.
Cache
FileEngine• In debug mode missing directories will now be automatically created to avoid unnecessary errors thrown.
Utility
General• pr() no longer outputs HTML when running in CLI mode.
Sanitize• Sanitize class has been deprecated.

2.4 Migration Guide

737

CakePHP Cookbook Documentation, Release 2.x

Validation• Validation::date() now supports the y and ym formats.
• The country code of Validation::phone() for Canada has been changed from can to ca to unify the country
codes for validation methods according to ISO 3166 (two letter codes).
CakeNumber• The currencies AUD, CAD and JPY have been added.
• The symbols for GBP and EUR are now UTF-8. If you upgrade a non-UTF-8 application, make sure that you
update the static $_currencies attribute with the appropriate HTML entity symbols (&#163; and &#8364;)
before you use those currencies.
• The fractionExponent option was added to CakeNumber::currency().
CakeTime• CakeTime::isPast() and CakeTime::isFuture() were added.
• CakeTime::timeAgoInWords() has two new options to customize the output strings: relativeString (defaults to %s ago) and absoluteString (defaults to on %s).
• CakeTime::timeAgoInWords() uses fuzzy terms when time is below thresholds.
Xml• New option pretty has been added to Xml::fromArray() to return nicely formatted Xml
Error
ErrorHandler• New configuration option skipLog has been added, to allow skipping certain Exception types
to be logged.
Configure::write('Exception.skipLog', array('NotFoundException','ForbiddenException')); will avoid these exceptions and the ones extending them to be be logged
when 'Exception.log' config is true
Routing
Router• Router::fullBaseUrl() was added together with App.fullBaseUrl Configure value.
FULL_BASE_URL which is now deprecated.

They replace• Router::parse() now parses query string arguments.

738

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

2.3 Migration Guide
2.3 Migration Guide
CakePHP 2.3 is a fully API compatible upgrade from 2.2. This page outlines the changes and improvements made in
2.3.
Constants
An application can now easily define CACHE and LOGS, as they are conditionally defined by CakePHP now.
Caching• FileEngine is always the default cache engine. In the past a number of people had difficulty setting up anddeploying APC correctly both in CLI + web. Using files should make setting up CakePHP simpler for new
developers.
• Configure::write(‘Cache.viewPrefix’, ‘YOURPREFIX’); has been added to core.php to allow multiple domains/languages per setup.
Component
AuthComponent• A new property AuthComponent::$unauthorizedRedirect has been added.
– For default true value user is redirected to referrer URL upon authorization failure.
– If set to a string or array user is redirected to that URL.
– If set to false a ForbiddenException exception is thrown instead of redirecting.
• A new authenticate adapter has been added to support blowfish/bcrypt hashed passwords. You can now use
Blowfish in your $authenticate array to allow bcrypt passwords to be used.
• AuthComponent::redirect() has been deprecated. Use AuthComponent::redirectUrl() instead.
PaginatorComponent• PaginatorComponent now supports the findType option. This can be used to specify what find method you
want used for pagination. This is a bit easier to manage and set than the 0’th index.
• PaginatorComponent now throws a NotFoundException when trying to access a page which is out of range (i.e.
requested page is greater than total page count).

2.3 Migration Guide

739

CakePHP Cookbook Documentation, Release 2.x

SecurityComponent• SecurityComponent now supports the unlockedActions option. This can be used to disable all security checks
for any actions listed in this option.
RequestHandlerComponent• RequestHandlerComponent::viewClassMap() has been added, which is used to map a type to view classname. You can add $settings['viewClassMap'] for automatically setting the correct viewClass based on
extension/content type.
CookieComponent• CookieComponent::check() was added. This method works the same as CakeSession::check() does.
Console• The server shell was added. You can use this to start the PHP5.4 webserver for your CakePHP application.
• Baking a new project now sets the application’s cache prefix to the name of the application.
I18n
L10n
• nld is now the default locale for Dutch as specified by ISO 639-3 and dut its alias. The locale folders have tobe adjusted accordingly (from /Locale/dut/ to /Locale/nld/ ).
• Albanian is now sqi, Basque is now eus, Chinese is now zho, Tibetan is now bod, Czech is now ces, Farsi is
now fas, French is now fra, Icelandic is now isl, Macedonian is now mkd, Malaysian is now msa, Romanianis now ron, Serbian is now srp and Slovak is now slk. The corresponding locale folders have to be adjusted,
as well.
Core
CakePlugin• CakePlugin::load() can now take a new ignoreMissing option. Setting it to true will prevent file include errors when you try to load routes or bootstrap but they don’t exist for a plugin. So essentially you
can now use the following statement which will load all plugins and their routes and bootstrap for whateverplugin it can find:: CakePlugin::loadAll(array(array('routes' => true, 'bootstrap' => true,'ignoreMissing' => true)))

740

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Configure• Configure::check() was added. This method works the same as CakeSession::check() does.
• ConfigReaderInterface::dump() was added. Please ensure any custom readers you have now implement adump() method.
• The $key parameter of IniReader::dump() now supports keys like PluginName.keyname similar to
PhpReader::dump().
Error
Exceptions• CakeBaseException was added, which all core Exceptions now extend. The base exception class also introduces
the responseHeader() method which can be called on created Exception instances to add headers for the
response, as Exceptions don’t reuse any response instance.
Model• Support for the biginteger type was added to all core datasources, and fixtures.
• Support for FULLTEXT indexes was added for the MySQL driver.
Models• Model::find('list') now sets the recursive based on the max containment depth or recursive value. Whenlist is used with ContainableBehavior.
• Model::find('first') will now return an empty array when no records are found.
Validation• Missing validation methods will always trigger errors now instead of only in development mode.
Network
SmtpTransport• TLS/SSL support was added for SMTP connections.

2.3 Migration Guide

741

CakePHP Cookbook Documentation, Release 2.x

CakeRequest• CakeRequest::onlyAllow() was added.
• CakeRequest::query() was added.
CakeResponse• CakeResponse::file() was added.
• The content types application/javascript, application/xml, application/rss+xml now also send the application
charset.
CakeEmail• The contentDisposition option was added to CakeEmail::attachments(). This allows you to disable theContent-Disposition header added to attached files.
HttpSocket• HttpSocket now verifies SSL certificates by default. If you are using self-signed certificates or connecting
through proxies you may need to use some of the new options to augment this behavior. See Handling SSL
certificates for more information.
• HttpResponse was renamed to HttpSocketResponse. This avoids a common issue with the HTTP PECL
extension. There is an HttpResponse class provided as well for compatibility reasons.
Routing
Router• Support for tel:, sms: were added to Router::url().
View• MediaView is deprecated, and you can use new features in CakeResponse to achieve the same results.
• Serialization in Json and Xml views has been moved to _serialize()• beforeRender and afterRender callbacks are now being called in Json and Xml views when using view templates.
• View::fetch() now has a $default argument. This argument can be used to provide a default value shoulda block be empty.
• View::prepend() has been added to allow prepending content to existing block.
• XmlView now uses the _rootNode view variable to customize the top level XML node.
• View::elementExists() was added. You can use this method to check if elements exist before using them.
• View::element() had the ignoreMissing option added. You can use this to suppress the errors triggered by
missing view elements.
• View::startIfEmpty() was added.

742

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Layout• The doctype for layout files in the app folder and the bake templates in the cake package has been changed fromXHTML to HTML5.
Helpers• New property Helper::$settings has been added for your helper setting. The $settings parameter of
Helper::__construct() is merged with Helper::$settings.
FormHelper• FormHelper::select() now accepts a list of values in the disabled attribute. Combined with 'multiple'=> 'checkbox', this allows you to provide a list of values you want disabled.
• FormHelper::postLink() now accepts a method key. This allows you to create link forms using HTTP methods other than POST.
• When creating inputs with FormHelper::input() you can now set the errorMessage option to false. This
will disable the error message display, but leave the error class names intact.
• The FormHelper now also adds the HTML5 required attribute to your input elements based on validation
rules for a field. If you have a “Cancel” button in your form which submits the form then you should add'formnovalidate' => true to your button options to prevent the triggering of validation in HTML. You
can also prevent the validation triggering for the whole form by adding 'novalidate' => true in yourFormHelper::create() options.
• FormHelper::input() now generates input elements of type tel and email based on field names if type
option is not specified.
HtmlHelper• HtmlHelper::getCrumbList() now has the separator, firstClass and lastClass options. These allow
you to better control the HTML this method generates.
TextHelper• TextHelper::tail() was added to truncate text starting from the end.
• ending in TextHelper::truncate() is deprecated in favor of ellipsis
PaginatorHelper• PaginatorHelper::numbers() now has a new option currentTag to allow specifying extra tag for wrapping
current page number.
• For methods: PaginatorHelper::prev() and PaginatorHelper::next() it is now possible to set the tag
option to false to disable the wrapper. Also a new option disabledTag has been added for these two methods.

2.3 Migration Guide743

CakePHP Cookbook Documentation, Release 2.x

Testing• A core fixture for the default cake_sessions table was added. You can use it by adding core.cake_sessions
to your fixture list.
• CakeTestCase::getMockForModel() was added. This simplifies getting mock objects for models.
Utility
CakeNumber• CakeNumber::fromReadableSize() was added.
• CakeNumber::formatDelta() was added.
• CakeNumber::defaultCurrency() was added.
Folder• Folder::copy() and Folder::move() now support the ability to merge the target and source directories in
addition to skip/overwrite.
String• String::tail() was added to truncate text starting from the end.
• ending in String::truncate() is deprecated in favor of ellipsis
Debugger• Debugger::exportVar() now outputs private and protected properties in PHP >= 5.3.0.
Security• Support for bcrypt179 was added. See the Security::hash() documentation for more information on how to
use bcrypt.
Validation• Validation::fileSize() was added.
179 https://codahale.com/how-to-safely-store-a-password/

744

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

ObjectCollection• ObjectCollection::attached()
wasdeprecated
in
favor
of
the
new
methodObjectCollection::loaded(). This unifies the access to the ObjectCollection as load()/unload() already replaced attach()/detach().

2.2 Migration Guide
2.2 Migration Guide
CakePHP 2.2 is a fully API compatible upgrade from 2.0/2.1. This page outlines the changes and improvements madefor 2.2.
Required steps to upgrade
When upgrading to CakePHP 2.2 its important to add a few new configuration values to app/Config/bootstrap.
php. Adding these will ensure consistent behavior with 2.1.x:// Enable the Dispatcher filters for plugin assets, and// CacheHelper.
Configure::write('Dispatcher.filters', array('AssetDispatcher','CacheDispatcher'));// Add logging configuration.
CakeLog::config('debug', array('engine' => 'FileLog','types' => array('notice', 'info', 'debug'),'file' => 'debug',));
CakeLog::config('error', array('engine' => 'FileLog','types' => array('warning', 'error', 'critical', 'alert', 'emergency'),'file' => 'error',));
You will also need to modify app/Config/core.php. Change the value of LOG_ERROR to LOG_ERR:
define('LOG_ERROR', LOG_ERR);
When using Model::validateAssociated() or Model::saveAssociated() and primary model validation fails,
the validation errors of associated models are no longer wiped out. Model::$validationErrors will now always
show all the errors. You might need to update your test cases to reflect this change.

2.2 Migration Guide

745

CakePHP Cookbook Documentation, Release 2.x

ConsoleI18N extract shell• An option was added to overwrite existing POT files by default:./Console/cake i18n extract --overwrite

Models• Model::find('count') will now call the custom find methods with $state = 'before' and$queryData['operation'] = 'count'. In many cases custom finds already return correct counts forpagination, but 'operation' key allows more flexibility to build other queries, or drop joins which are required
for the custom finder itself. As the pagination of custom find methods never worked quite well it required
workarounds for this in the model level, which are now no longer needed.
• Model::find('first') will now return an empty array when no records are found.
Datasources• Dbo datasources now supports real nested transactions. If you need to use this feature in your application, enable
it using ConnectionManager::getDataSource('default')->useNestedTransactions = true;
Testing• The webrunner now includes links to re-run a test with debug output.
• Generated test cases for Controller now subclass ControllerTestCase.
Error Handling• When repeat exceptions, or exception are raised when rendering error pages, the new error layout will be used.
It’s recommended to not use additional helpers in this layout as its intended for development level errors only.
This fixes issues with fatal errors in rendering error pages due to helper usage in the default layout.
• It is important to copy the app/View/Layouts/error.ctp into your app directory. Failing to do so will makeerror page rendering fail.
• You can now configure application specific console error handling. By setting Error.consoleHandler, andException.consoleHandler you can define the callback that will handle errors/exceptions raised in console
applications.
• The handler configured in Error.handler and Error.consoleHandler will receive fatal error codes (ie.
E_ERROR, E_PARSE, E_USER_ERROR).

746

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Exceptions• The NotImplementedException was added.
Core
Configure• Configure::dump() was added. It is used to persist configuration data in durable storage like files. Both
PhpReader and IniReader work with it.
• A new config parameter ‘Config.timezone’ is available in which you can set users’ timezone string. eg. You
can do Configure::write('Config.timezone', 'Europe/Paris'). If a method of CakeTime class iscalled with $timezone parameter as null and ‘Config.timezone’ is set, then the value of ‘Config.timezone’ will
be used. This feature allows you to set users’ timezone just once instead of passing it each time in function calls.
Controller
AuthComponent• The options for adapters defined in AuthComponent::$authenticate now accepts a contain option. This isused to set containable options for when user records are loaded.
CookieComponent• You can now encrypt cookie values with the rijndael cipher. This requires the mcrypt180 extension to be installed.
Using rijndael gives cookie values actual encryption, and is recommended in place of the XOR cipher available
in previous releases. The XOR cipher is still the default cipher scheme to maintain compatibility with previous
releases. You can read more in the Security::rijndael() documentation.
Pagination• Paginating custom finders will now return correct counts, see Model changes for more info.
Network
CakeEmail• CakeEmail::charset() and CakeEmail::headerCharset() were added.
• Legacy Japanese encodings are now handled correctly. ISO-2202-JP is used when the encoding is
ISO-2202-JP-MS which works around a number of issues in mail clients when dealing with the CP932 andShift_JIS encodings.
• CakeEmail::theme() was added.
• CakeEmail::domain() was added. You can use this method to set the domain name used when sending email
from a CLI script or if you want to control the hostname used to send email.
• You can now define theme and helpers in your EmailConfig class.
180 https://www.php.net/mcrypt

2.2 Migration Guide747

CakePHP Cookbook Documentation, Release 2.x

CakeRequest• CakeRequest will now automatically decode application/x-www-form-urlencoded request bodies on PUTand DELETE requests. This data will be available as $this->data just like POST data is.
Utility
Set• The Set class is now deprecated, and replaced by the Hash class. Set will not be removed until 3.0.
• Set::expand() was added.
Hash
The Hash class was added in 2.2. It replaced Set providing a more consistent, reliable and performant API to doing
many of the same tasks Set does. See the Hash page for more detail.
CakeTime• The $userOffset parameter has been replaced with $timezone parameter in all relevant functions. So insteadof numeric offset you can now pass in a timezone string or DateTimeZone object. Passing numeric offsets for$timezone parameter is still possible for backwards compatibility.
• CakeTime::timeAgoInWords() had the accuracy option added. This option allows you to specify how accurate formatted times should be.
• New methods added:– CakeTime::toServer()– CakeTime::timezone()– CakeTime::listTimezones()• The $dateString parameter in all methods now accepts a DateTime object.
Helpers
FormHelper• FormHelper now better handles adding required classes to inputs. It now honors the on key.
• FormHelper::radio() now supports an empty which works similar to the empty option on select().
• Added FormHelper::inputDefaults() to set common properties for each of the inputs generated by thehelper748

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

TimeHelper• Since 2.1, TimeHelper uses the CakeTime class for all its relevant methods. The $userOffset parameter has
been replaced with $timezone parameter.
• TimeHelper::timeAgoInWords() has the element option added. This allows you to specify an HTML element to wrap the formatted time.
HtmlHelper• HtmlHelper::tableHeaders() now supports setting attributes per table cell.
Routing
Dispatcher• Event listeners can now be attached to the dispatcher calls, those will have the ability to change the request
information or the response before it is sent to the client. Check the full documentation for this new features in
Dispatcher Filters• With the addition of Dispatcher Filters you’ll need to update app/Config/bootstrap.php. See Required steps
to upgrade.
Router• Router::setExtensions() has been added. With the new method you can now add more extensions to beparsed, for example within a plugin routes file.
Cache
Redis Engine
A new caching engine was added using the phpredis extension181 it is configured similarly to the Memcache engine.
Cache groups
It is now possible to tag or label cache keys under groups. This makes it simpler to mass-delete cache entries associatedto the same label. Groups are declared at configuration time when creating the cache engine:
Cache::config(array('engine' => 'Redis',...
'groups' => array('post', 'comment', 'user')));
You can have as many groups as you like, but keep in mind they cannot be dynamically modified.
The Cache::clearGroup() class method was added. It takes the group name and deletes all entries labeled with thesame string.
181 https://github.com/phpredis/phpredis

2.2 Migration Guide

749

CakePHP Cookbook Documentation, Release 2.x

Log
Changes in CakeLog now require, some additional configuration in your app/Config/bootstrap.php. See Required
steps to upgrade, and Logging.
• The CakeLog class now accepts the same log levels as defined in RFC 5424182 . Several convenience methods
have also been added:– CakeLog::emergency($message, $scope = array())– CakeLog::alert($message, $scope = array())– CakeLog::critical($message, $scope = array())– CakeLog::error($message, $scope = array())– CakeLog::warning($message, $scope = array())– CakeLog::notice($message, $scope = array())– CakeLog::info($message, $scope = array())– CakeLog::debug($message, $scope = array())• A third argument $scope has been added to CakeLog::write. See Logging Scopes.
• A new log engine: ConsoleLog has been added.
Model Validation• A new object ModelValidator was added to delegate the work of validating model data, it should be transparent to the application and fully backwards compatible. It also exposes a rich API to add, modify and remove
validation rules. Check docs for this object in Data Validation.
• Custom validation functions in your models need to have “public” visibility so that they are accessible byModelValidator.
• New validation rules added:– Validation::naturalNumber()– Validation::mimeType()– Validation::uploadError()

2.1 Migration Guide
2.1 Migration Guide
CakePHP 2.1 is a fully API compatible upgrade from 2.0. This page outlines the changes and improvements made for2.1.
182 https://tools.ietf.org/html/rfc5424

750

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

AppController, AppHelper, AppModel and AppShell
These classes are now required to be part of the app directory, as they were removed from the CakePHP core. If you
do not already have these classes, you can use the following while upgrading:// app/View/Helper/AppHelper.php
App::uses('Helper', 'View');
class AppHelper extends Helper {}// app/Model/AppModel.php
App::uses('Model', 'Model');class AppModel extends Model {}// app/Controller/AppController.php
App::uses('Controller', 'Controller');class AppController extends Controller {}// app/Console/Command/AppShell.php
App::uses('Shell', 'Console');class AppShell extends Shell {}If your application already has these files/classes you don’t need to do anything. Additionally if you were using thecore PagesController, you would need to copy this to your app/Controller directory as well.
.htaccess files
The default .htaccess files have changed, you should remember to update them or update your webservers URL
re-writing scheme to match the changes done in .htaccess
Models• The beforeDelete callback will be fired before behaviors beforeDelete callbacks. This makes it consistent withthe rest of the events triggered in the model layer.
• Model::find('threaded') now accepts $options['parent'] if using other field then parent_id. Also
if the model has TreeBehavior attached and set up with other parent field, the threaded find will by default use
that.
• Parameters for queries using prepared statements will now be part of the SQL dump.
• Validation arrays can now be more specific with when a field is required. The required key now accepts create
and update. These values will make a field required when creating or updating.
• Model now has a schemaName property.
If your application switches datasources by modifying
Model::$useDbConfig you should also modify schemaName or use Model::setDataSource() method
which handles this for you.

2.1 Migration Guide

751

CakePHP Cookbook Documentation, Release 2.x

CakeSession
Changed in version 2.1.1: CakeSession no longer sets the P3P header, as this is the responsibility of your application.
Behaviors
TranslateBehavior• I18nModel has been moved into a separate file.
Exceptions
The default exception rendering now includes more detailed stack traces including file excerpts and argument dumps
for all functions in the stack.
Utility
Debugger• Debugger::getType() has been added. It can be used to get the type of variables.
• Debugger::exportVar() has been modified to create more readable and useful output.
debug()
debug() now uses Debugger internally. This makes it consistent with Debugger, and takes advantage of improvements
made there.
Set• Set::nest() has been added. It takes in a flat array and returns a nested array
File• File::info() includes filesize & mimetype information.
• File::mime() was added.
Cache• CacheEngine has been moved into a separate file.

752

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Configure• ConfigReaderInterface has been moved into a separate file.
App• App::build() now has the ability to register new packages using App::REGISTER. See Add new packages toan application for more information.
• Classes that could not be found on configured paths will be searched inside APP as a fallback path. This makes
autoloading nested directories in app/Vendor easier.
ConsoleTest Shell
A new TestShell has been added. It reduces the typing required to run unit tests, and offers a file path based UI:./Console/cake test app Model/Post./Console/cake test app Controller/PostsController./Console/cake test Plugin View/Helper/MyHelper
The old testsuite shell and its syntax are still available.
General• Generated files no longer contain timestamps with the generation datetime.
Routing
Router• Routes can now use a special /** syntax to include all trailing arguments as a single passed argument. See thesection on Connecting Routes for more information.
• Router::resourceMap() was added.
• Router::defaultRouteClass() was added. This method allows you to set the default route class used for all
future routes that are connected.
Network
CakeRequest• Added is('requested') and isRequested() for detecting requestAction.

2.1 Migration Guide

753

CakePHP Cookbook Documentation, Release 2.x

CakeResponse• Added CakeResponse::cookie() for setting cookies.
• Added a number of methods for Fine tuning HTTP cacheController
Controller• Controller::$uses was modified the default value is now true instead of false. Additionally different values
are handled slightly differently, but will behave the same in most cases.
– true Will load the default model and merge with AppController.
– An array will load those models and merge with AppController.
– An empty array will not load any models other than those declared in the base class.
– false will not load any models, and will not merge with the base class either.
Components
AuthComponent• AuthComponent::allow() no longer accepts allow('*') as a wildcard for all actions. Just use allow().
This unifies the API between allow() and deny().
• recursive option was added to all authentication adapters. Allows you to more easily control the associations
stored in the session.
AclComponent• AclComponent no longer lowercases and inflects the class name used for Acl.classname. Instead it uses theprovided value as is.
• Acl backend implementations should now be put in Controller/Component/Acl.
• Acl implementations should be moved into the Component/Acl directory from Component. For example if yourAcl class was called CustomAclComponent, and was in Controller/Component/CustomAclComponent.
php. It should be moved into Controller/Component/Acl/CustomAcl.php, and be named CustomAcl.
• DbAcl has been moved into a separate file.
• IniAcl has been moved into a separate file.
• AclInterface has been moved into a separate file.

754

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Helpers
TextHelper• TextHelper::autoLink(), TextHelper::autoLinkUrls(), TextHelper::autoLinkEmails() now
HTML escape their input by default. You can control this with the escape option.
HtmlHelper• HtmlHelper::script() had a block option added.
• HtmlHelper::scriptBlock() had a block option added.
• HtmlHelper::css() had a block option added.
• HtmlHelper::meta() had a block option added.
• The $startText parameter of HtmlHelper::getCrumbs() can now be an array. This gives more control andflexibility over the first crumb link.
• HtmlHelper::docType() now defaults to HTML5.
• HtmlHelper::image() now has a fullBase option.
• HtmlHelper::media() has been added. You can use this method to create HTML5 audio/video elements.
• plugin syntax support has been added for HtmlHelper::script(), HtmlHelper::css(),
HtmlHelper::image(). You can now easily link to plugin assets using Plugin.asset.
• HtmlHelper::getCrumbList() had the $startText parameter added.
View• View::$output is deprecated.
• $content_for_layout is deprecated. Use $this->fetch('content'); instead.
• $scripts_for_layout is deprecated. Use the following instead:echo $this->fetch('meta');echo $this->fetch('css');
echo $this->fetch('script');$scripts_for_layout is still available, but the view blocks API gives a more extensible & flexible replacement.
• The Plugin.view syntax is now available everywhere. You can use this syntax anywhere you reference thename of a view, layout or element.
• The $options['plugin'] option for element() is deprecated. You should use Plugin.element_name instead.

2.1 Migration Guide

755

CakePHP Cookbook Documentation, Release 2.x

Content type views
Two new view classes have been added to CakePHP. A new JsonView and XmlView allow you to easily generate XML
and JSON views. You can learn more about these classes in the section on JSON and XML views
Extending views
View has a new method allowing you to wrap or ‘extend’ a view/element/layout with another file. See the section on
Extending Views for more information on this feature.
ThemesThe ThemeView class is deprecated in favor of the View class. Simply setting $this->theme = 'MyTheme' will
enable theme support, and all custom View classes which extend from ThemeView should extend View.
View blocks
View blocks are a flexible way to create slots or blocks in your views. Blocks replace $scripts_for_layout with amore robust and flexible API. See the section on Using view blocks for more information.
Helpers
New callbacks
Two new callbacks have been added to Helpers.
Helper::beforeRenderFile() andHelper::afterRenderFile() these new callbacks are fired before/after every view fragment is rendered.
This includes elements, layouts and views.
CacheHelper• <!--nocache--> tags now work inside elements correctly.
FormHelper• FormHelper now omits disabled fields from the secured fields hash.
SecurityComponent and disabled inputs easier.

This makes working with• The between option when used in conjunction with radio inputs, now behaves differently. The between value
is now placed between the legend and first input elements.
• The hiddenField option with checkbox inputs can now be set to a specific value such as ‘N’ rather than just 0.
• The for attribute for date + time inputs now reflects the first generated input. This may result in the for attributechanging for generated datetime inputs.
• The type attribute for FormHelper::button() can be removed now. It still defaults to ‘submit’.
• FormHelper::radio() now allows you to disable all options. You can do this by setting either 'disabled'=> true or 'disabled' => 'disabled' in the $attributes array.

756

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

PaginatorHelper• PaginatorHelper::numbers() now has a currentClass option.
Testing• Web test runner now displays the PHPUnit version number.
• Web test runner now defaults to displaying app tests.
• Fixtures can be created in different datasources other than $test.
• Models loaded using the ClassRegistry and using another datasource will get their datasource name prepended
with test_ (e.g datasource master will try to use test_master in the testsuite)• Test cases are generated with class specific setup methods.
Events• A new generic events system has been built and it replaced the way callbacks were dispatched. This should not
represent any change to your code.
• You can dispatch your own events and attach callbacks to them at will, useful for inter-plugin communicationand easier decoupling of your classes.

New Features in CakePHP 2.1
ModelsModel::saveAll(), Model::saveAssociated(), Model::validateAssociated()
Model::saveAll() and friends now support passing the fieldList for multiple models. Example:$this->SomeModel->saveAll($data, array('fieldList' => array('SomeModel' => array('field_1'),'AssociatedModel' => array('field_2', 'field_3'))));
Model::saveAll() and friends now can save unlimited levels deep. Example:$data = array('Article' => array('title' => 'My first article'),'Comment' => array(
array('body' => 'Comment 1', 'user_id' => 1),
array('body' => 'Save a new user as well','User' => array('first' => 'mad', 'last' => 'coder'))),);$this->SomeModel->saveAll($data, array('deep' => true));

2.1 Migration Guide

757

CakePHP Cookbook Documentation, Release 2.x

View
View Blocks
View Blocks are a mechanism to allow the inclusion of slots of content, whilst allowing child view classes or elements
to provide custom content for that block.
Blocks are output by calling the fetch method on the View. For example, the following can be placed in your View/
Layouts/default.ctp file:<?php echo $this->fetch('my_block'); ?>This will echo the content of the block if available, or an empty string if it is undefined.
Setting the content of a block can be done in a number of ways. A simple assignment of data can be done using assign:<?php $this->assign('my_block', 'Hello Block'); ?>Or you can use it to capture a section of more complex content:<?php $this->start('my_block'); ?><h1>Hello Block!</h1><p>This is a block of content</p><p>Page title: <?php echo $title_for_layout; ?></p><?php $this->end(); ?>Block capturing also supports nesting:<?php $this->start('my_block'); ?><h1>Hello Block!</h1><p>This is a block of content</p><?php $this->start('second_block'); ?><p>Page title: <?php echo $title_for_layout; ?></p><?php $this->end(); ?><?php $this->end(); ?>ThemeView
In 2.1, the use of ThemeView is deprecated in favor of using the View class itself. ThemeView is now a stub class.
All custom pathing code has been moved into the View class, meaning that it is now possible for classes extending
the View class to automatically support themes. Whereas before we might set the $viewClass Controller property to
Theme, it is now possible to enable themes by simply setting the $theme property. Example:
App::uses('Controller', 'Controller');class AppController extends Controller {public $theme = 'Example';}
All View classes which extended ThemeView in 2.0 should now simply extend View.

758

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

JsonView
A new view class that eases the output of JSON content.
Previously, it was necessary to create a JSON layout (APP/View/Layouts/json/default.ctp) and a corresponding
view for each action that would output JSON. This is no longer required with JsonView.
The JsonView is used like any other view class, by defining it on the controller. Example:
App::uses('Controller', 'Controller');class AppController extends Controller {public $viewClass = 'Json';}Once you have setup the controller, you need to identify what content should be serialized as JSON, by setting the view
variable _serialize. Example:$this->set(compact('users', 'posts', 'tags'));$this->set('_serialize', array('users', 'posts'));
The above example would result in only the users and posts variables being serialized for the JSON output, like so:{"users": [...], "posts": [...]}
There is no longer any need to create view ctp files in order to display Json content.
Further customization of the output can be achieved by extending the JsonView class with your own custom view class
if required.
The following example wraps the result with {results:

...

}:

App::uses('JsonView', 'View');
class ResultsJsonView extends JsonView {public function render($view = null, $layout = null) {$result = parent::render($view, $layout);
if (isset($this->viewVars['_serialize'])) {return json_encode(array('results' => json_decode($result)));}return $result;}}

XmlView
Much like the JsonView, the XmlView requires you to set the _serialize view variable in order to indicate what
information should be serialized into XML for output:$this->set(compact('users', 'posts', 'tags'));$this->set('_serialize', array('users', 'posts'));
The above example would result in only the users and posts variables being serialized for the XML output, like so:<response><users>...</users><posts>...</posts></response>
Note that the XmlView adds a response node to wrap all serialized content.
2.1 Migration Guide

759

CakePHP Cookbook Documentation, Release 2.x

Conditional View Rendering
Several new methods were added to CakeRequest to ease the task of setting correct HTTP headers to foster HTTP
caching. You can now define our caching strategy using the expiration or validation HTTP cache model, or combineboth. Now there are specific methods in CakeRequest to fine-tune Cache-Control directives, set the entity tag (Etag),
set the Last-Modified time and much more.
When those methods are combined with having the RequestHandlerComponent enabled in your controller, the component will automatically decide if the response is already cached in the client and will send a 304 Not Modified status
code before rendering the view. Skipping the view rendering process saves CPU cycles and memory.
class ArticlesController extends AppController {public $components = array('RequestHandler');
public function view($id) {$article = $this->Article->findById($id);$this->response->modified($article['Article']['modified']);$this->set(compact('article'));}}In the above example the view will not be rendered if the client sent the header If-Modified-Since, and the response will
have a 304 status.
Helpers
To allow easier use outside of the View layer, methods from TimeHelper, TextHelper, and NumberHelper helpers
have been extracted to CakeTime, String, and CakeNumber classes respectively.
To use the new utility classes:
class AppController extends Controller {public function log($msg) {$msg .= String::truncate($msg, 100);parent::log($msg);}}
You can override the default class to use by creating a new class in your APP/Utility folder, e.g.: Utility/
MyAwesomeStringClass.php, and specify it in engine key:// Utility/MyAwesomeStringClass.php
class MyAwesomeStringClass extends String {// my truncate is better than yours
public static function truncate($text, $length = 100, $options = array()) {return null;}}// Controller/AppController.php
class AppController extends Controller {public $helpers = array('Text' => array((continues on next page)

760

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)'engine' => 'MyAwesomeStringClass',),);}

HtmlHelper
A new function HtmlHelper::media() has been added for HTML5’s audio/video element generation.

2.0 Migration Guide
2.0 Migration Guide
This page summarizes the changes from CakePHP 1.3 that will assist in a project migration to 2.0, as well as for adeveloper reference to get up to date with the changes made to the core since the CakePHP 1.3 branch. Be sure to readthe other pages in this guide for all the new features and API changes.
Tip: Be sure to checkout the Upgrade shell included in the 2.0 core to help you migrate your 1.3 code to 2.0.

PHP Version Support
CakePHP 2.x supports PHP Version 5.2.8 and above. PHP4 support has been dropped. For developers that are stillworking with production PHP4 environments, the CakePHP 1.x versions continue to support PHP4 for the lifetime of
their development and support lifetime.
The move to PHP 5 means all methods and properties have been updated with visibility keywords. If your code is
attempting access to private or protected methods from a public scope, you will encounter errors.
While this does not really constitute a large framework change, it means that access to tighter visibility methods andvariables is now not possible.
File and Folder naming
In CakePHP 2.0 we rethought the way we are structuring our files and folders. Given that PHP 5.3 is supporting
namespaces we decided to prepare our code base for adopting in a near future this PHP version, so we adopted thehttps://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md. At first we glanced at the internal structureof CakePHP 1.3 and realized that after all these years there was no clear organization in the files, nor did the directory
structure really hint where each file should be located. With this change we would be allowed to experiment a little
with (almost) automatic class loading for increasing the overall framework performance.
Biggest roadblock for achieving this was maintaining some sort of backwards compatibility in the way the classes areloaded right now, and we definitely did not want to become a framework of huge class prefixes, having class names like
My_Huge_Class_Name_In_Package. We decided adopting a strategy of keeping simple class names while offeringa very intuitive way of declaring class locations and clear migration path for future PHP 5.3 version of CakePHP. First
let’s highlight the main changes in file naming standard we adopted:

2.0 Migration Guide

761

CakePHP Cookbook Documentation, Release 2.x

File names
All files containing classes should be named after the class it contains. No file should contain more than one class. So,
no more lowercasing and underscoring your file names. Here are some examples:• my_things_controller.php becomes MyThingsController.php• form.php (a Helper) becomes FormHelper.php• session.php (a Component) becomes SessionComponent.php
This makes file naming a lot more clear and consistent across applications, and also avoids a few edge cases where thefile loader would get confused in the past and load files it should not.
Folder Names
Most folders should be also CamelCased, especially when containing classes. Think of namespaces, each folder represents a level in the namespacing hierarchy, folders that do not contain classes, or do not constitute a namespace onthemselves, should be lowercased.
CamelCased Folders:• Config• Console• Controller• Controller/Component• Lib• Locale• Model• Model/Behavior• Plugin• Test• Vendor• View• View/Helper
lowercased Folders:• tmp• webroot

762

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

htaccess (URL Rewriting)In your app/webroot/.htaccess replace line RewriteRule ^(.*)$ index.php?url=$1 [QSA,L] with
RewriteRule ^(.*)$ index.php [QSA,L]
AppController / AppModel / AppHelper / AppShell
The app/app_controller.php, app/app_model.php, app/app_helper.php are now located and named as app/
Controller/AppController.php, app/Model/AppModel.php and app/View/Helper/AppHelper.php respectively.
Also all shell/task now extend AppShell. You can have your custom AppShell.php at app/Console/Command/
AppShell.php
Internationalization / Localization__() (Double underscore shortcut function) always returns the translation (not echo anymore).
If you want to echo the result of the translation, use:echo __('My Message');
This change includes all shortcut translation methods:
__()
__n()
__d()
__dn()__dc()__dcn()__c()
Alongside this, if you pass additional parameters, the translation will call sprintf183 with these parameters before returning. For example:// Will return something like "Called: MyClass:myMethod"echo __('Called: %s:%s', $className, $methodName);
It is valid for all shortcut translation methods.
More information about the specifiers, you can see in sprintf184 function.
Class location and constants changed
The constants APP and CORE_PATH have consistent values between the web and console environments. In previous
versions of CakePHP these values changed depending on your environment.
183 https://www.php.net/manual/en/function.sprintf.php
184 https://www.php.net/manual/en/function.sprintf.php

2.0 Migration Guide

763

CakePHP Cookbook Documentation, Release 2.x

Basics.php• getMicrotime() has been removed. Use the native microtime(true) instead.
• e() was removed. Use echo.
• r() was removed. Use str_replace.
• a() was removed. Use array()• aa() was removed. Use array()• up() was removed. Use strtoupper()• low() was removed. Use strtolower()• params() was removed. It was not used anywhere in CakePHP.
• ife() was removed. Use a ternary operator.
• uses() was removed. Use App::import() instead.
• Compatibility functions for PHP4 have been removed.
• PHP5 constant has been removed.
• Global var called $TIME_START was removed use the constant TIME_START or $_SERVER['REQUEST_TIME']
instead.
Removed Constants
A number of constants were removed, as they were no longer accurate, or duplicated.
• APP_PATH• BEHAVIORS• COMPONENTS• CONFIGS• CONSOLE_LIBS• CONTROLLERS• CONTROLLER_TESTS• ELEMENTS• HELPERS• HELPER_TESTS• LAYOUTS• LIB_TESTS• LIBS• MODELS• MODEL_TESTS• SCRIPTS• VIEWS

764

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

CakeRequest
This new class encapsulates the parameters and functions related to an incoming request. It replaces many featuresinside Dispatcher, RequestHandlerComponent and Controller. It also replaces $this->params array in all places.
CakeRequest implements ArrayAccess so many interactions with the old params array do not need to change. See
the CakeRequest new features for more information.
Request handling, $_GET[‘url’] and .htaccess files
CakePHP no longer uses $_GET['url'] for handling application request paths.
Instead it uses$_SERVER['PATH_INFO']. This provides a more uniform way of handling requests between servers with URL
rewriting and those without. Because of these changes, you’ll need to update your .htaccess files and app/webroot/index.php, as these files were changed to accommodate the changes. Additionally $this->params['url']['url']
no longer exists. Instead you should be using $this->request->url to access the same value. This attribute now contains
the url without the leading slash / prepended.
Note: For the homepage itself (http://domain/) $this->request->url now returns boolean false instead of /. Make
sure you check on that accordingly:
if (!$this->request->url) {} // instead of $this->request->url === '/'Components
Component is now the required base class for all components. You should update your components and their constructors, as both have changed:class PrgComponent extends Component {public function __construct(ComponentCollection $collection,$settings = array()) {parent::__construct($collection, $settings);}}
As with helpers it is important to call parent::__construct() in components with overridden constructors. Settings
for a component are also passed into the constructor now, and not the initialize() callback. This makes getting
well constructed objects easier, and allows the base class to handle setting the properties up.
Since settings have been moved to the component constructor, the initialize() callback no longer receives$settings as its 2nd parameter. You should update your components to use the following method signature:public function initialize(Controller $controller) { }Additionally, the initialize() method is only called on components that are enabled. This usually means componentsthat are directly attached to the controller object.

2.0 Migration Guide

765

CakePHP Cookbook Documentation, Release 2.x

Deprecated callbacks removed
All the deprecated callbacks in Component have not been transferred to ComponentCollection. Instead you should use
the trigger() method to interact with callbacks. If you need to trigger a callback you could do so by calling:$this->Components->trigger('someCallback', array(&$this));Changes in disabling components
In the past you were able to disable components via $this->Auth->enabled = false; for example. In CakePHP 2.0
you should use the ComponentCollection’s disable method, $this->Components->disable(‘Auth’);. Using the enabled
property will not work.
AclComponent• AclComponent implementations are now required to implement AclInterface.
• AclComponent::adapter() has been added to allow runtime modification of the ACL implementation thecomponent uses.
• AclComponent::grant() has been deprecated, it will be removed in a future version.
AclComponent::allow() instead.

Use• AclComponent::revoke() has been deprecated, it will be removed in a future version. Use AclComponent::deny() instead.
RequestHandlerComponent
Many of RequestHandlerComponent’s methods are just proxies for CakeRequest methods. The following methods
have been deprecated and will be removed in future versions:• isSsl()• isAjax()• isPost()• isPut()• isFlash()• isDelete()• getReferer()• getClientIp()• accepts(), prefers(), requestedWith() All deal in mapped content types now. They no longer work with
mime-types. You can use RequestHandler::setContent() to create new content types.
• RequestHandler::setContent() no longer accepts an array as a single argument, you must supply botharguments.

766

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

SecurityComponent
SecurityComponent no longer handles Basic and Digest Authentication. These are both handled by the new AuthComponent. The following methods have been removed from SecurityComponent:• requireLogin()• generateDigestResponseHash()• loginCredentials()• loginRequest()• parseDigestAuthData()
In addition the following properties were removed:• $loginUsers• $requireLogin
Moving these features to AuthComponent was done to provide a single place for all types of authentication and to
streamline the roles of each component.
AuthComponent
The AuthComponent was entirely re-factored for 2.0, this was done to help reduce developer confusion and frustration.
In addition, AuthComponent was made more flexible and extensible. You can find out more in the Authentication
guide.
EmailComponent
The EmailComponent has been deprecated and has created a new library class to send e-mails. See CakeEmail Emailchanges for more details.
SessionComponent
Session component has lost the following methods.
• activate()• active()• __start()cakeError removed
The cakeError() method has been removed. It’s recommended that you switch all uses of cakeError to use exceptions. cakeError was removed because it was simulating exceptions. Instead of simulation, real exceptions are usedin CakePHP 2.0.

2.0 Migration Guide

767

CakePHP Cookbook Documentation, Release 2.x

Error handling
The error handling implementation has dramatically changed in 2.0. Exceptions have been introduced throughout
the framework, and error handling has been updated to offer more control and flexibility. You can read more in the
Exceptions and Error Handling section.
Lib classes
App
The API for App::build() has changed to App::build($paths, $mode). It now allows you to either append,
prepend or reset/replace existing paths. The $mode param can take any of the following 3 values: App::APPEND,
App::PREPEND, App::RESET. The default behavior of the function remains the same (ie. Prepending new paths toexisting list).
App::path()• Now supports plugins, App::path(‘Controller’, ‘Users’) will return the folder location of the controllers in theUsers plugin.
• Won’t merge core paths anymore, it will only return paths defined in App::build() or default ones in app (orcorresponding plugin).
App::build()• Will not merge app path with core paths anymore.
App::objects()• Now supports plugins, App::objects(‘Users.Model’) will return the models in plugin Users.
• Returns array() instead of false for empty results or invalid types.
• Does not return core objects anymore, App::objects(‘core’) will return array().
• Returns the complete class name.
App class lost the following properties, use method App::path() to access their value• App::$models• App::$behaviors• App::$controllers• App::$components• App::$datasources• App::$libs• App::$views• App::$helpers• App::$plugins

768

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x• App::$vendors• App::$locales• App::$shells
App::import()• No longer looks for classes recursively, it strictly uses the values for the paths defined in App::build().
• Will not be able to load App::import(‘Component’, ‘Component’) use App::uses(‘Component’, ‘Controller’);• Using App::import(‘Lib’, ‘CoreClass’) to load core classes is no longer possible.
• Importing a non-existent file, supplying a wrong type or package name, or null values for $name and $file parameters will result in a false return value.
• App::import(‘Core’, ‘CoreClass’) is no longer supported, use App::uses() instead and let the class autoloading
do the rest.
• Loading Vendor files does not look recursively in the vendors folder, it will also no longer convert the file tounderscored as it did in the past.
App::core()• First parameter is no longer optional, it will always return one path
• It can’t be used anymore to get the vendors paths• It will only accept new style package names
Class loading with App::uses()
Although there has been a huge refactoring in how the classes are loaded, in very few occasions you will need to change
your application code to respect the way you were used to doing it. The biggest change is the introduction of a new
method:App::uses('AuthComponent', 'Controller/Component');
We decided the function name should emulate PHP 5.3’s use keyword, just as a way of declaring where a class nameshould be located. The first parameter of App::uses() is the complete name of the class you intend to load, andthe second one, the package name (or namespace) where it belongs to. The main difference with CakePHP 1.3’s
App::import() is that the former won’t actually import the class, it will just setup the system so when the class isused for the first time it will be located.
Some examples on using App::uses() when migrating from App::import():
App::import('Controller', 'Pages');// becomes
App::uses('PagesController', 'Controller');
App::import('Component', 'Auth');// becomes
App::uses('AuthComponent', 'Controller/Component');
App::import('View', 'Media');(continues on next page)

2.0 Migration Guide

769

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)// becomes
App::uses('MediaView', 'View');
App::import('Core', 'Xml');// becomes
App::uses('Xml', 'Utility');
App::import('Datasource', 'MongoDb.MongoDbSource');// becomes
App::uses('MongoDbSource', 'MongoDb.Model/Datasource');
All classes that were loaded in the past using App::import('Core', $class); will need to be loaded using
App::uses() referring to the correct package. See the API to locate the classes in their new folders. Some examples:
App::import('Core', 'CakeRoute');// becomes
App::uses('CakeRoute', 'Routing/Route');App::import('Core', 'Sanitize');// becomes
App::uses('Sanitize', 'Utility');
App::import('Core', 'HttpSocket');// becomes
App::uses('HttpSocket', 'Network/Http');
In contrast to how App::import() worked in the past, the new class loader will not locate classes recursively. This led
to an impressive performance gain even on develop mode, at the cost of some seldom used features that always caused
side effects. To be clear again, the class loader will only fetch the class in the exact package in which you told it to findit.
App::build() and core paths
App::build() will not merge app paths with core paths anymore.
Examples:App::build(array('controllers' => array('/full/path/to/controllers')));//becomes
App::build(array('Controller' => array('/full/path/to/Controller')));
App::build(array('helpers' => array('/full/path/to/controllers')));//becomes
App::build(array('View/Helper' => array('/full/path/to/View/Helper')));

770

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

CakeLog• Log streams now need to implement CakeLogInterface. Exceptions will be raised if a configured logger does
not.
Cache
• Cache is now a static class, it no longer has a getInstance() method.
• CacheEngine is now an abstract class. You cannot directly create instances of it anymore.
• CacheEngine implementations must extend CacheEngine, exceptions will be raised if a configured class doesnot.
• FileCache now requires trailing slashes to be added to the path setting when you are modifying a cache configuration.
• Cache no longer retains the name of the last configured cache engine. This means that operations you want tooccur on a specific engine need to have the $config parameter equal to the config name you want the operation
to occur on.
Cache::config('something');
Cache::write('key', $value);// would become
Cache::write('key', $value, 'something');

Router• You can no longer modify named parameter settings with Router::setRequestInfo(). You should use
Router::connectNamed() to configure how named parameters are handled.
• Router no longer has a getInstance() method. It is a static class, call its methods and properties statically.
• Router::getNamedExpressions() is deprecated. Use the new router constants. Router::ACTION,
Router::YEAR, Router::MONTH, Router::DAY, Router::ID, and Router::UUID instead.
• Router::defaults() has been removed. Delete the core routes file inclusion from your applications routes.php
file to disable default routing. Conversely if you want default routing, you will have to add an include to Cake/
Config/routes.php in your routes file.
• Whenusing
Router::parseExtensions()
theextension
parameterisnolonger$this->params['url']['ext']. Instead it is available at $this->request->params['ext'].

under• Default plugin routes have changed. Plugin short routes are no longer built in for any actions other than index.
Previously /users and /users/add would map to the UsersController in the Users plugin. In 2.0, only theindex action is given a short route. If you wish to continue using short routes, you can add a route like:
Router::connect('/users/:action',array('controller' => 'users', 'plugin' => 'users'));
To your routes file for each plugin you need short routes on.
Your app/Config/routes.php file needs to be updated adding this line at the bottom of the file:

2.0 Migration Guide

771

CakePHP Cookbook Documentation, Release 2.x

require CAKE . 'Config' . DS . 'routes.php';
This is needed in order to generate the default routes for your application. If you do not wish to have such routes, orwant to implement your own standard you can include your own file with custom router rules.
Dispatcher• Dispatcher has been moved inside of cake/libs, you will have to update your app/webroot/index.php file.
• Dispatcher::dispatch() now takes two parameters. The request and response objects. These should beinstances of CakeRequest & CakeResponse or a subclass thereof.
• Dispatcher::parseParams() now only accepts a CakeRequest object.
• Dispatcher::baseUrl() has been removed.
• Dispatcher::getUrl() has been removed.
• Dispatcher::uri() has been removed.
• Dispatcher::$here has been removed.
Configure• Configure::read() with no parameter no longer returns the value of ‘debug’ instead it returns all values in
Configure. Use Configure::read('debug'); if you want the value of debug.
• Configure::load() now requires a ConfigReader to be setup. Read Loading configuration files for more
information.
• Configure::store() now writes values to a given Cache configuration. Read Loading configuration files formore information.
Scaffold• Scaffold ‘edit’ views should be renamed to ‘form’. This was done to make scaffold and bake templates consistent.
– views/scaffolds/edit.ctp -> View/Scaffolds/form.ctp– views/posts/scaffold.edit.ctp -> View/Posts/scaffold.form.ctp
Xml• The class Xml was completely re-factored. Now this class does not manipulate data anymore, and it is a wrapper
to SimpleXMLElement. You can use the following methods:– Xml::build(): static method that you can pass an xml string, array, path to file or url. The result will be
a SimpleXMLElement instance or an exception will be thrown in case of error.
– Xml::fromArray(): static method that returns a SimpleXMLElement from an array.
– Xml::toArray(): static method that returns an array from SimpleXMLElement.
You should see the Xml documentation for more information on the changes made to the Xml class.

772

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Inflector• Inflector no longer has a getInstance() method.
• Inflector::slug() no longer supports the $map argument. Use Inflector::rules() to define transliteration rules.
CakeSession
CakeSession is now a fully static class, both SessionHelper and SessionComponent are wrappers and sugar for it.
It can now easily be used in models or other contexts. All of its methods are called statically.
Session configuration has also changed see the session section for more information
HttpSocket• HttpSocket doesn’t change the header keys. Following other places in core, the HttpSocket does not change theheaders. RFC 2616185 says that headers are case insensitive, and HttpSocket preserves the values the remote
host sends.
• HttpSocket returns responses as objects now. Instead of arrays, HttpSocket returns instances of HttpResponse.
See the HttpSocket documentation for more information.
• Cookies are stored internally by host, not per instance. This means that, if you make two requests to different
servers, cookies from domain1 won’t be sent to domain2. This was done to avoid possible security problems.
Helpers
Constructor changed
In order to accommodate View being removed from the ClassRegistry, the signature of Helper::__construct() waschanged. You should update any subclasses to use the following:
public function __construct(View $View, $settings = array())
When overriding the constructor you should always call parent::__construct as well. Helper::__construct stores theview instance at $this->_View for later reference. The settings are not handled by the parent constructor.
HelperCollection added
After examining the responsibilities of each class involved in the View layer, it became clear that View was handlingmuch more than a single task. The responsibility of creating helpers is not central to what View does, and was moved
into HelperCollection. HelperCollection is responsible for loading and constructing helpers, as well as triggering callbacks on helpers. By default, View creates a HelperCollection in its constructor, and uses it for subsequent operations.
The HelperCollection for a view can be found at $this->Helpers
The motivations for refactoring this functionality came from a few issues.
• View being registered in ClassRegistry could cause registry poisoning issues when requestAction or the EmailComponent were used.
• View being accessible as a global symbol invited abuse.
185 https://datatracker.ietf.org/doc/html/rfc2616.html

2.0 Migration Guide773

CakePHP Cookbook Documentation, Release 2.x• Helpers were not self contained. After constructing a helper, you had to manually construct several other objects
in order to get a functioning object.
You can read more about HelperCollection in the Collections documentation.
Deprecated properties
The following properties on helpers are deprecated, you should use the request object properties or Helper methods
instead of directly accessing these properties as they will be removed in a future release.
• Helper::$webroot is deprecated, use the request object’s webroot property.
• Helper::$base is deprecated, use the request object’s base property.
• Helper::$here is deprecated, use the request object’s here property.
• Helper::$data is deprecated, use the request object’s data property.
• Helper::$params is deprecated, use the $this->request instead.
XmlHelper, AjaxHelper and JavascriptHelper removed
The AjaxHelper and JavascriptHelper have been removed as they were deprecated in version 1.3. The XmlHelper was
removed, as it was made obsolete and redundant with the improvements to Xml. The Xml class should be used to replace
previous usage of XmlHelper.
The AjaxHelper, and JavascriptHelper are replaced with the JsHelper and HtmlHelper.
JsHelper• JsBaseEngineHelper is now abstract, you will need to implement all the methods that previously generated
errors.
PaginatorHelper• PaginatorHelper::sort() now takes the title and key arguments in the reverse order. $key will always be
first now. This was done to prevent needing to swap arguments when adding a second one.
• PaginatorHelper had a number of changes to the paging params used internally. The default key has been removed.
• PaginatorHelper now supports generating links with paging parameters in the querystring.
There have been a few improvements to pagination in general. For more information on that you should read the newpagination features page.

774

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

FormHelper$selected parameter removed
The $selected parameter was removed from several methods in FormHelper. All methods now support a$attributes['value'] key now which should be used in place of $selected. This change simplifies theFormHelper methods, reducing the number of arguments, and reduces the duplication that $selected created. The
effected methods are:
• FormHelper::select()• FormHelper::dateTime()• FormHelper::year()• FormHelper::month()• FormHelper::day()• FormHelper::hour()• FormHelper::minute()• FormHelper::meridian()
Default URLs on forms is the current action
The default url for all forms, is now the current url including passed, named, and querystring parameters. You canoverride this default by supplying $options['url'] in the second parameter of $this->Form->create().
FormHelper::hidden()
Hidden fields no longer remove the class attribute. This means that if there are validation errors on hidden fields, theerror-field class name will be applied.
CacheHelper
CacheHelper has been fully decoupled from View, and uses helper callbacks to generate caches. You should remember
to place CacheHelper after other helpers that modify content in their afterRender and afterLayout callbacks. If
you don’t some changes will not be part of the cached content.
CacheHelper also no longer uses <cake:nocache> to indicate un-cached regions. Instead it uses special HTML/XML
comments. <!--nocache--> and <!--/nocache-->. This helps CacheHelper generate valid markup and still perform the same functions as before. You can read more CacheHelper and View changes.

2.0 Migration Guide

775

CakePHP Cookbook Documentation, Release 2.x

Helper Attribute format more flexible
The Helper class has more 3 protected attributes:• Helper::_minimizedAttributes: array with minimized attributes (ie: array('checked', 'selected',...));• Helper::_attributeFormat: how attributes will be generated (ie: %s="%s");• Helper::_minimizedAttributeFormat: how minimized attributes will be generated: (ie %s="%s")
By default the values used in CakePHP 1.3 were not changed. But now you can use boolean attributes from HTML, like
<input type="checkbox" checked />. To this, just change $_minimizedAttributeFormat in your AppHelper
to %s.
To use with Html/Form helpers and others, you can write:$this->Form->checkbox('field', array('checked' => true, 'value' => 'some_value'));Other facility is that minimized attributes can be passed as item and not as key. For example:$this->Form->checkbox('field', array('checked', 'value' => 'some_value'));Note that checked have a numeric key.
Controller• Controller’s constructor now takes two parameters. A CakeRequest, and CakeResponse objects. These objects
are used to populate several deprecated properties and will be set to $request and $response inside the controller.
• Controller::$webroot is deprecated, use the request object’s webroot property.
• Controller::$base is deprecated, use the request object’s base property.
• Controller::$here is deprecated, use the request object’s here property.
• Controller::$data is deprecated, use the request object’s data property.
• Controller::$params is deprecated, use the $this->request instead.
• Controller::$Component has been moved to Controller::$Components. See the Collections documentation for more information.
• Controller::$view has been renamed to Controller::$viewClass. Controller::$view is now used tochange which view file is rendered.
• Controller::render() now returns a CakeResponse object.
The deprecated properties on Controller will be accessible through a __get() method. This method will be removed
in future versions, so it’s recommended that you update your application.
Controller now defines a maxLimit for pagination. This maximum limit is set to 100, but can be overridden in the$paginate options.

776

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Pagination
Pagination has traditionally been a single method in Controller, this created a number of problems though.
Pagination was hard to extend, replace, or modify. For 2.0 pagination has been extracted into a component. Controller::paginate() still exists, and serves as a convenience method for loading and using the
PaginatorComponent.
For more information on the new features offered by pagination in 2.0, see the Pagination documentation.
View
View no longer registered in ClassRegistry
The view being registered ClassRegistry invited abuse and affectively created a global symbol. In 2.0 each Helper
receives the current View instance in its constructor. This allows helpers access to the view in a similar fashion as in
the past, without creating global symbols. You can access the view instance at $this->_View in any helper.
Deprecated properties• View::$webroot is deprecated, use the request object’s webroot property.
• View::$base is deprecated, use the request object’s base property.
• View::$here is deprecated, use the request object’s here property.
• View::$data is deprecated, use the request object’s data property.
• View::$params is deprecated, use the $this->request instead.
• View::$loaded has been removed. Use the HelperCollection to access loaded helpers.
• View::$model has been removed. This behavior is now on Helper• View::$modelId has been removed. This behavior is now on Helper• View::$association has been removed. This behavior is now on Helper• View::$fieldSuffix has been removed. This behavior is now on Helper• View::entity() has been removed. This behavior is now on Helper• View::_loadHelpers() has been removed, used View::loadHelpers() instead.
• How View::element() uses caching has changed, see below for more information.
• View callbacks have been shifted around, see below for more information• API for View::element() has changed. Read here for more info.
The deprecated properties on View will be accessible through a __get() method. This method will be removed infuture versions, so it’s recommended that you update your application.

2.0 Migration Guide

777

CakePHP Cookbook Documentation, Release 2.x

Removed methods• View::_triggerHelpers() Use $this->Helpers->trigger() instead.
• View::_loadHelpers() Use $this->loadHelpers() instead. Helpers now lazy load their own helpers.
Added methods• View::loadHelper($name, $settings = array()); Load a single helper.
• View::loadHelpers() Loads all the helpers indicated in View::$helpers.
View->Helpers
By default View objects contain a HelperCollection at $this->Helpers.
Themes
To use themes in your Controller you no longer set public $view = 'Theme';. Use public $viewClass ='Theme'; instead.
Callback positioning changes
beforeLayout used to fire after scripts_for_layout and content_for_layout were prepared. In 2.0, beforeLayout is fired
before any of the special variables are prepared, allowing you to manipulate them before they are passed to the layout.
The same was done for beforeRender. It is now fired well before any view variables are manipulated. In addition tothese changes, helper callbacks always receive the name of the file about to be rendered. This combined with helpers
being able to access the view through $this->_View and the current view content through $this->_View->output
gives you more power than ever before.
Helper callback signature changes
Helper callbacks now always get one argument passed in. For beforeRender and afterRender it is the view file being
rendered. For beforeLayout and afterLayout it is the layout file being rendered. Your helpers function signatures shouldlook like:public function beforeRender($viewFile) {}public function afterRender($viewFile) {}public function beforeLayout($layoutFile) {}public function afterLayout($layoutFile) {(continues on next page)

778

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)}
Element caching, and view callbacks have been changed in 2.0 to help provide you with more flexibility and consistency.
Read more about those changes.
CacheHelper decoupled
In previous versions there was a tight coupling between CacheHelper and View. For 2.0 this coupling has been
removed and CacheHelper just uses callbacks like other helpers to generate full page caches.
CacheHelper <cake:nocache> tags changed
In previous versions, CacheHelper used a special <cake:nocache> tag as markers for output that should not be partof the full page cache. These tags were not part of any XML schema, and were not possible to validate in HTML or
XML documents. For 2.0, these tags have been replaced with HTML/XML comments:<cake:nocache> becomes <!--nocache--></cake:nocache> becomes <!--/nocache-->
The internal code for full page view caches has also changed, so be sure to clear out view cache files when updating.
MediaView changes
MediaView::render() now forces download of unknown file types instead of just returning false. If you want you
provide an alternate download filename you now specify the full name including extension using key ‘name’ in thearray parameter passed to the function.
PHPUnit instead of SimpleTest
All of the core test cases and supporting infrastructure have been ported to use PHPUnit 3.7. Of course you can continue
to use SimpleTest in your application by replacing the related files. No further support will be given for SimpleTest
and it is recommended that you migrate to PHPUnit as well. For some additional information on how to migrate yourtests see PHPUnit migration hints.
No more group tests
PHPUnit does not differentiate between group tests and single test cases in the runner. Because of this, the group
test options, and support for old style group tests has been removed. It is recommended that GroupTests be ported to
PHPUnit_Framework_Testsuite subclasses. You can find several examples of this in CakePHP’s test suite. Group
test related methods on TestManager have also been removed.

2.0 Migration Guide

779

CakePHP Cookbook Documentation, Release 2.x

Testsuite shell
The testsuite shell has had its invocation simplified and expanded. You no longer need to differentiate between caseand group. It is assumed that all tests are cases. In the past you would have done cake testsuite app casemodels/post you can now do cake testsuite app Model/Post.
The testsuite shell has been refactored to use the PHPUnit CLI tool. It now supports all the command line options
supported by PHPUnit. cake testsuite help will show you a list of all possible modifiers.
Model
Model relationships are now lazy loaded. You can run into a situation where assigning a value to a nonexistent model
property will throw errors:$Post->inexistentProperty[] = 'value';
will throw the error “Notice: Indirect modification of overloaded property $inexistentProperty has no effect”. Assigning
an initial value to the property solves the issue:$Post->nonexistentProperty = array();$Post->nonexistentProperty[] = 'value';
Or just declare the property in the model class:class Post {public $nonexistentProperty = array();}
Either of these approaches will solve the notice errors.
The notation of find() in CakePHP 1.2 is no longer supported. Finds should use notation $model->find('type',
array(PARAMS)) in CakePHP 1.3.
• Model::$_findMethods is now Model::$findMethods. This property is now public and can be modified bybehaviors.
Database objects
CakePHP 2.0 introduces some changes to Database objects that should not greatly affect backwards compatibility. The
biggest one is the adoption of PDO for handling database connections. If you are using a vanilla installation of PHP
5 you will already have installed the needed extensions, but you may need to activate individual extensions for eachdriver you wish to use.
Using PDO across all DBOs let us homogenize the code for each one and provide more reliable and predictable behavior
for all drivers. It also allowed us to write more portable and accurate tests for database related code.
The first thing users will probably miss is the “affected rows” and “total rows” statistics, as they are not reported due tothe more performant and lazy design of PDO, there are ways to overcome this issue but very specific to each database.
Those statistics are not gone, though, but could be missing or even inaccurate for some drivers.
A nice feature added after the PDO adoption is the ability to use prepared statements with query placeholders using thenative driver if available.

780

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

List of Changes• DboMysqli was removed, we will support DboMysql only.
• API for DboSource::execute has changed, it will now take an array of query values as second parameter:
public function execute($sql, $params = array(), $options = array())
became:public function execute($sql, $options = array(), $params = array())
third parameter is meant to receive options for logging, currently it only understands the “log” option.
• DboSource::value() looses its third parameter, it was not used anyways• DboSource::fetchAll() now accepts an array as second parameter, to pass values to be bound to the query, thirdparameter was dropped. Example:$db->fetchAll('SELECT* from users
WHERE
username = ?
ANDpassword = ?',array('jhon', '12345'));$db->fetchAll('SELECT* from users
WHERE
username = :usernameANDpassword = :password',array('username' => 'jhon', 'password' => '12345'));
The PDO driver will automatically escape those values for you.
• Database statistics are collected only if the “fullDebug” property of the corresponding DBO is set to true.
• New method DboSource::getConnection() will return the PDO object in case you need to talk to the driver
directly.
• Treatment of boolean values changed a bit to make it more cross-database friendly, you may need to change yourtest cases.
• PostgreSQL support was immensely improved, it now correctly creates schemas, truncate tables, and is easier towrite tests using it.
• DboSource::insertMulti() will no longer accept sql string, just pass an array of fields and a nested array of values
to insert them all at once
• TranslateBehavior was refactored to use model virtualFields, this makes the implementation more portable.
• All tests cases with MySQL related stuff were moved to the corresponding driver test case. This left the
DboSourceTest file a bit skinny.

2.0 Migration Guide

781

CakePHP Cookbook Documentation, Release 2.x• Transaction nesting support. Now it is possible to start a transaction several times. It will only be committed if
the commit method is called the same amount of times.
• SQLite support was greatly improved. The major difference with cake 1.3 is that it will only support SQLite 3.x. It is a great alternative for development apps, and quick at running test cases.
• Boolean column values will be casted to PHP native boolean type automatically, so make sure you update yourtest cases and code if you were expecting the returned value to be a string or an integer: If you had a “published”
column in the past using MySQL all values returned from a find would be numeric in the past, now they are strictboolean values.
Behaviors
BehaviorCollection• BehaviorCollection no longer strtolower()'s mappedMethods. Behavior mappedMethods are now casesensitive.
AclBehavior and TreeBehavior• No longer supports strings as configuration. Example:
public $actsAs = array('Acl' => 'Controlled','Tree' => 'nested');became:public $actsAs = array('Acl' => array('type' => 'Controlled'),'Tree' => array('type' => 'nested'));

Plugins
Plugins no longer magically append their plugin prefix to components, helpers and models used within them. You must
be explicit with the components, models, and helpers you wish to use. In the past:
public $components = array('Session', 'Comments');
Would look in the controller’s plugin before checking app/core components. It will now only look in the app/corecomponents. If you wish to use objects from a plugin you must put the plugin name:public $components = array('Session', 'Comment.Comments');
This was done to reduce hard to debug issues caused by magic misfiring. It also improves consistency in an application,
as objects have one authoritative way to reference them.

782

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Plugin App Controller and Plugin App Model
The plugin AppController and AppModel are no longer located directly in the plugin folder. They are now placed into
the plugin’s Controller and Model folders as such:/app/Plugin/Comment/Controller
CommentAppController.php/ModelCommentAppModel.php

ConsoleMuch of the console framework was rebuilt for 2.0 to address many of the following issues:• Tightly coupled.
• It was difficult to make help text for shells.
• Parameters for shells were tedious to validate.
• Plugin tasks were not reachable.
• Objects with too many responsibilities.
Backwards incompatible Shell API changes• Shell no longer has an AppModel instance. This AppModel instance was not correctly built and was problematic.
• Shell::_loadDbConfig() has been removed. It was not generic enough to stay in Shell. You can use the
DbConfigTask if you need to ask the user to create a db config.
• Shells no longer use $this->Dispatcher to access stdin, stdout, and stderr. They have ConsoleOutput andConsoleInput objects to handle that now.
• Shells lazy load tasks, and use TaskCollection to provide an interface similar to that used for Helpers, Components, and Behaviors for on the fly loading of tasks.
• Shell::$shell has been removed.
• Shell::_checkArgs() has been removed. Configure a ConsoleOptionParser• Shells no longer have direct access to ShellDispatcher. You should use the ConsoleInput, andConsoleOutput objects instead. If you need to dispatch other shells, see the section on ‘Invoking other shells
from your shell’.

2.0 Migration Guide

783

CakePHP Cookbook Documentation, Release 2.x

Backwards incompatible ShellDispatcher API changes• ShellDispatcher no longer has stdout, stdin, stderr file handles.
• ShellDispatcher::$shell has been removed.
• ShellDispatcher::$shellClass has been removed.
• ShellDispatcher::$shellName has been removed.
• ShellDispatcher::$shellCommand has been removed.
• ShellDispatcher::$shellPaths has been removed, use App::path('shells'); instead.
• ShellDispatcher no longer uses ‘help’ as a magic method that has special status. Instead use the --help/-h
options, and an option parser.
Backwards incompatible Shell Changes• Bake’s ControllerTask no longer takes public and admin as passed arguments. They are now options, indicatedlike --admin and --public.
It’s recommended that you use the help on shells you use to see what if any parameters have changed. It’s also recommended that you read the console new features for more information on new APIs that are available.
Debugging
The debug() function now defaults to outputting HTML safe strings. This is disabled if being used in the console.
The $showHtml option for debug() can be set to false to disable HTML-safe output from debug.
ConnectionManager
ConnectionManager::enumConnectionObjects() will now return the current configuration for each connection
created, instead of an array with filename, class name and plugin, which wasn’t really useful.
When defining database connections you will need to make some changes to the way configs were defined in the past.
Basically in the database configuration class, the key “driver” is not accepted anymore, only “datasource”, in order tomake it more consistent. Also, as the datasources have been moved to packages you will need to pass the package they
are located in. Example:
public $default = array('datasource' => 'Database/Mysql','persistent' => false,'host' => 'localhost','login' => 'root','password' => 'root','database' => 'cake',);

784

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

New Features in CakePHP 2.0
ModelsThe model construction process has been made lighter. Model associations are now lazy loaded, applications with lotsof models and associations will see great time reductions in the bootstrap process.
Now models won’t require a database connection in the construction process. The database will be accessed for the
first time only when a find operation is issued or information for one of the columns is required.
View
View::$output
View will now always have the last rendered content (view or layout) accessible through $this->output. In helpers
you can use $this->_View->output. Modifying this property will change the content that comes out of the view
rendering.
Helpers
HtmlHelper• getCrumbList() Creates breadcrumb links wrapped in <li> elements.
• loadConfig() has moved from Helper to HtmlHelper class. This method now uses the new reader classes(see 2.0 Configure) to load your config file. As an option you can pass the path as second parameter (app/Config is default). To simplify, you can set the configuration file (and the reader) in Controller::$helpers(example below) to load on helper constructor. In configuration file you can set the below keys:• tags Should be an array with key value;• minimizedAttributes Should be a list;• docTypes Should be an array with key value;• attributeFormat Should be a string;• minimizedAttributeFormat Should be a string.
Example of how to set configuration file on controller:
public $helpers = array('Html' => array(// Option one: an array with filename and reader name'configFile' => array('config_file', 'php')// Option two: a string with filename. The PhpReader will be used'configFile' => 'config_file'));

2.0 Migration Guide

785

CakePHP Cookbook Documentation, Release 2.x

FormHelper• FormHelper now supports all HTML5 input types and custom input types. Just use the input type you want as
the method on the helper. For example range() would create an input with type = range.
• postLink() and postButton() Creates link/button to access some page using HTTP method POST. With this,in your controller you can avoid some action, like delete, to be accessed by GET method.
• select() with multiple = checkbox, now treats the 'id' attribute as a prefix for all the generated options.
Libs
CakeRequest
CakeRequest is a new class introduced in 2.0. It encapsulates commonly used request introspection methods andreplaces the params array with a more useful object. Read more about CakeRequest.
CakeResponse
CakeResponse is a new class introduced in 2.0. It encapsulates commonly used methods and properties in the HTTP
response your application generates. It consolidates several features in CakePHP. Read more about CakeResponse.
CakeSession, SessionComponent
CakeSession and the SessionComponent have had a number of changes, see the session section for more information.
RouterRoutes can return full URLs
Route objects can now return full URLs, and Router will not further modify them beyond adding the query string andfragment elements. For example this could be used to create routes to handle subdomains, or enabling https/http flags.
An example of a route class that supports subdomains would be:class SubdomainRoute extends CakeRoute {public function match($params) {$subdomain = isset($params['subdomain']) ? $params['subdomain'] : null;
unset($params['subdomain']);$path = parent::match($params);
if ($subdomain) {$path = 'http://' . $subdomain . '.localhost' . $path;}
return $path;}}
When creating links you could do the following to make links pointing at other subdomains.

786

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

echo $this->Html->link('Other domain',array('subdomain' => 'test', 'controller' => 'posts', 'action' => 'add'));
The above would create a link with http://test.localhost/posts/add as the url.
Xml
Xml has had a number of changes. Read more about Xml class.
New Lib features
Configure readers
Configure can now be configured to load configuration files from a variety of sources and formats. The Configuration
section contains more information about the changes made to configure.
Configure::read() without any arguments allows you to read all values from configure, instead of just the debug
value.
Error and exception handling
CakePHP 2.0 has had Exceptions and Error Handling handling rebuilt, to be more flexible and give more power todevelopers.
String::wrap()
String::wrap() was added to help make fixed width formatting of text easier. It’s used in Shells whenever you use
Shell::wrapText().
debug()
debug() no longer outputs HTML in the console. Instead it makes output like the following:########## DEBUG ##########
Array([0] => test)###########################
This should improve readability of debug() on the command line.

2.0 Migration Guide

787

CakePHP Cookbook Documentation, Release 2.x

Components
Components received a similar treatment to helpers and behaviors, Component is now the base class for components.
Read more about the component changes.
RequestHandler
RequestHandler was heavily refactored due to the introduction of CakeRequest. These changes allowed for some
new features to be introduced as well.
Automatic parsing of Accept headers
If a client sends a single Accept mime type that matches one of the extensions enabled in :php:class`Router`,
RequestHandler will treat it the same an extension. This expands CakePHP’s support for REST style endpoints.
To use this feature start off by enabling extensions in app/Config/routes.php:
Router::parseExtensions('json', 'xml');Once you have created layouts and views for your extensions, you will be able to visit a url like posts/view/1 and send
Accept: application/json in the headers to receive the JSON version of that URL.
CookieComponent
CookieComponent now supports HTTP only cookies.
You can enable their use by setting$this->Cookie->httpOnly = true;. Having HTTP only cookies will make them inaccessible from thebrowser.
Security Component CSRF separation
CakePHP has had CSRF protection since 1.2. For 2.0 the existing CSRF has a new more paranoid mode, and is its
own standalone feature. In the past CSRF features were coupled with form tampering safe-guards. Developers often
disabled validatePost in order to do dynamic forms, disabling the CSRF protection at the same time. For 2.0 CSRF
checking has been separated from form tampering giving you greater control.
For more information see CSRF protection
Controller
Controllers now have access to request and response objects. You can read more about these objects on their specificpages.

788

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

ConsoleThe console for CakePHP 2.0 was almost entirely rebuilt. Several new features as well as some backwards incompatiblechanges were made. Read more about console changes.
Pagination
Pagination now provides a default maxLimit for pagination at 100.
This limit can be overridden with the paginate variable on Controller:$this->paginate = array('maxLimit' => 1000);
This default is provided to prevent user URL manipulation causing excessive strain on the database for subsequent
requests, where a user would edit the ‘limit’ parameter to a very large number.
Aliasing
You can now alias helpers, components and behaviors to use your class instead of a different one. This means that you
can very easily make a MyHtml helper and not need to replace every instance of $this->Html in your views. To dothis, pass the ‘className’ key along with your class, like you would with models:public $helpers = array('Html' => array('className' => 'MyHtml'));
Similarly, you can alias components for use in your controllers:public $components = array('Email' => array('className' => 'QueueEmailer'));
Calls to the Email component would call the QueueEmailer component instead. Finally, you can alias behaviors as
well:public $actsAs = array('Containable' => array('className' => 'SuperContainable'));
Because of the way 2.0 utilizes collections and shares them across the application, any classes you alias will be usedthroughout your application. Whenever your application tries to access the alias, it will access your class. For instance,
when we aliased the Html helper in the example above, any helpers that use the Html helper or elements that load the
Html helper, will use MyHtml instead.

2.0 Migration Guide

789

CakePHP Cookbook Documentation, Release 2.x

ConnectionManager
A new method ConnectionManager::drop() was added to allow removing connections at runtime.

PHPUnit Migration Hints
Migrating your test cases to PHPUnit 3.7186 will hopefully be a fairly pain free transition. However, there are a few
known differences between test cases under PHPUnit and SimpleTest187 .
Differences between SimpleTest
There are a number of differences between SimpleTest and PHPUnit. The following is an attempt to list the mostfrequently encountered differences.
startCase() and endCase()
These methods are no longer supported. Use the static methods PHPUnit provides: setupBeforeClass andtearDownAfterClass.
start(), end(), before() and after()
These methods were part of SimpleTest’s test case initialization. start() and end() have no replacements. You can
use setUp() and tearDown() to replace before() and after().
setUp() and tearDown()
In the past the methods setUp, tearDown, startTest and endTest where supported, and caused confusion as theylooked almost like the same thing but in some cases you should use one or the other.
In the new CakePHP test suite, it is recommended to use only setUp and tearDown. The methods startTest and endTest
are still supported but are deprecated.
getTests
The method getTests is no longer supported. You can use filters instead. The web test runner now takes an additional
query string parameter that allows you to specify a basic regular expression. This regular expression is used to restrict
the methods that are run:e.g. filter=myMethod
Only tests containing the string myMethod will be run on the next refresh. The cake test shell also supports a –filter
option to filter methods.
186 https://www.phpunit.de/manual/current/en/
187 https://www.simpletest.org/

790

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Assertion methods
Many of the assertion methods have slightly different names between PHPUnit and SimpleTest. Where possible
CakeTestCase provides a wrapper for the SimpleTest method names. These compatibility wrappers will be removed
in 2.1.0. The following methods will be affected.
• assertEqual -> assertEquals• assertNotEqual -> assertNotEquals• assertPattern -> assertRegExp• assertIdentical -> assertSame• assertNotIdentical -> assertNotSame• assertNoPattern -> assertNotRegExp• assertNoErrors -> no replacement• expectError -> setExpectedException• expectException -> setExpectedException• assertReference -> assertSame• assertIsA -> assertTypeSome methods take their arguments in different orders, be sure to check the methods you are using when updating
them.
Mock expectations
Mock objects are dramatically different between PHPUnit and SimpleTest. There is no compatibility wrapper between
them. Updating mock object usage can be a painful process but we hope the following tips help you in your migration.
It’s highly recommended you familiarize yourself with the PHPUnit Mock object188 documentation.
Replacing method calls
The following regular expressions should help update some of your more straightforward mock object expectations.
Replace expectOnce() no params
expectOnce\(([^\)]+)\);
expects(\$this->once())->method($1);
188 https://www.phpunit.de/manual/current/en/test-doubles.html#test-doubles.mock-objects

2.0 Migration Guide

791

CakePHP Cookbook Documentation, Release 2.x

Replace expectOnce() with params
expectOnce\(([^,]+), array\((.+)\)\);
expects(\$this->once())->method($1)->with($2);

Replace expectAt()
expectAt\((\d+), (.+), array\((.+)\)\);
expects(\$this->at($1))->method($2)->with($3);

Replace expectNever
expectNever\(([^\)]+)\);
expects(\$this->never())->method($1);

Replace setReturnValue
setReturnValue\(([^,]+), (.+)\);
expects(\$this->once())->method($1)->will($this->returnValue($2));

Replace setReturnValueAt
setReturnValueAt((\d+), ([^,]+), (.+));
expects(\$this->at($1))->method($2)->will($this->returnValue($3));

Group tests
Group tests have been removed as PHPUnit treats individual test cases and test suites as composable entities in therunner. You can place group tests inside the cases directory and use PHPUnit_Framework_TestSuite as a baseclass. An example Testsuite would look like:class AllJavascriptHelpersTest extends PHPUnit_Framework_TestSuite {/*** Suite define the tests for this suite** @return void*/
public static function suite() {$suite = new PHPUnit_Framework_TestSuite('JsHelper and all Engine Helpers');$helperTestPath = CORE_TEST_CASES . DS . 'View' . DS . 'Helper' . DS;$suite->addTestFile($helperTestPath . 'JsHelperTest.php');$suite->addTestFile($helperTestPath . 'JqueryEngineHelperTest.php');(continues on next page)

792

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x(continued from previous page)$suite->addTestFile($helperTestPath . 'MootoolsEngineHelperTest.php');$suite->addTestFile($helperTestPath . 'PrototypeEngineHelperTest.php');
return $suite;}}
TestManger no longer has methods to add tests to group tests either. It is recommended that you use the methods
PHPUnit offers.

Migration from 1.2 to 1.3
Migrating from CakePHP 1.2 to 1.3
This guide summarizes many of the changes necessary when migrating from a 1.2 to 1.3 CakePHP core. Each section
contains relevant information for the modifications made to existing methods as well as any methods that have been
removed/renamed.
App File Replacements (important)• webroot/index.php: Must be replaced due to changes in bootstrapping process.
• config/core.php: Additional settings have been put in place which are required for PHP 5.3.
• webroot/test.php: Replace if you want to run unit tests.
Removed Constants
The following constants have been removed from CakePHP. If your application depends on them you must define themin app/config/bootstrap.php• CIPHER_SEED - It has been replaced with Configure class var Security.cipherSeed which should be changed
in app/config/core.php• PEAR• INFLECTIONS• VALID_NOT_EMPTY• VALID_EMAIL• VALID_NUMBER• VALID_YEAR
Configuration and application bootstrapping
Bootstrapping Additional Paths.
In your app/config/bootstrap.php you may have variables like $pluginPaths or $controllerPaths. There is a new
way to add those paths. As of 1.3 RC1 the $pluginPaths variables will no longer work. You must use App::build()
to modify paths.

Migration from 1.2 to 1.3

793CakePHP Cookbook Documentation, Release 2.x

App::build(array('plugins' => array('/full/path/to/plugins/','/next/full/path/to/plugins/'),'models' => array('/full/path/to/models/','/next/full/path/to/models/'),'views' => array('/full/path/to/views/','/next/full/path/to/views/'),'controllers' => array('/full/path/to/controllers/','/next/full/path/to/controllers/'),'datasources' => array('/full/path/to/datasources/','/next/full/path/to/datasources/'),'behaviors' => array('/full/path/to/behaviors/','/next/full/path/to/behaviors/'),'components' => array('/full/path/to/components/','/next/full/path/to/components/'),'helpers' => array('/full/path/to/helpers/','/next/full/path/to/helpers/'),'vendors' => array('/full/path/to/vendors/','/next/full/path/to/vendors/'),'shells' => array('/full/path/to/shells/','/next/full/path/to/shells/'),'locales' => array('/full/path/to/locale/','/next/full/path/to/locale/'),'libs' => array('/full/path/to/libs/','/next/full/path/to/libs/')));
Also changed is the order in which bootstrapping occurs. In the past app/config/core.php was loaded after app/config/bootstrap.php. This caused any App::import() in an application bootstrap to be un-cached and consider-794

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

ably slower than a cached include. In 1.3 core.php is loaded and the core cache configs are created before bootstrap.php
is loaded.
Loading custom inflections
inflections.php has been removed, it was an unnecessary file hit, and the related features have been refactored intoa method to increase their flexibility. You now use Inflector::rules() to load custom inflections:
Inflector::rules('singular', array('rules' => array('/^(bil)er$/i' => '\1','/^(inflec|contribu)tors$/i' => '\1ta'),'uninflected' => array('singulars'),'irregular' => array('spins' => 'spinor')));
Will merge the supplied rules into the infection sets, with the added rules taking precedence over the core rules.
File renames and internal changes
Library Renames
Core libraries of libs/session.php, libs/socket.php, libs/model/schema.php and libs/model/behavior.php have been renamed so that there is a better mapping between filenames and main classes contained within (as well as dealing withsome name-spacing issues):• session.php -> cake_session.php– App::import(‘Core’, ‘Session’) -> App::import(‘Core’, ‘CakeSession’)• socket.php -> cake_socket.php– App::import(‘Core’, ‘Socket’) -> App::import(‘Core’, ‘CakeSocket’)• schema.php -> cake_schema.php– App::import(‘Model’, ‘Schema’) -> App::import(‘Model’, ‘CakeSchema’)• behavior.php -> model_behavior.php– App::import(‘Core’, ‘Behavior’) -> App::import(‘Core’, ‘ModelBehavior’)
In most cases, the above renaming will not affect userland code.
Inheritance from Object
The following classes no longer extend Object:• Router• Set• Inflector• Cache• CacheEngine
If you were using Object methods from these classes, you will need to not use those methods.

Migration from 1.2 to 1.3795CakePHP Cookbook Documentation, Release 2.x

Controller & Components
Controller• Controller::set() no longer changes variables from $var_name to $varName. Variables always appear in
the view exactly as you set them.
• Controller::set('title', $var) no longer sets $title_for_layout when rendering the layout.
$title_for_layout is still populated by default.
But if you want to customize it, use$this->set('title_for_layout', $var).
• Controller::$pageTitle has been removed. Use $this->set('title_for_layout', $var); instead.
• Controller has two new methods startupProcess and shutdownProcess. These methods are responsible forhandling the controller startup and shutdown processes.
Component• Component::triggerCallback has been added. It is a generic hook into the component callback process.
It supplants Component::startup(), Component::shutdown() and Component::beforeRender() as thepreferred way to trigger callbacks.
CookieComponent• del is deprecated use delete
AclComponent + DbAcl
Node reference checks done with paths are now less greedy and will no longer consume intermediary nodes when doing
searches. In the past given the structure:
ROOT/
Users/
Users/
edit
The path ROOT/Users would match the last Users node instead of the first. In 1.3, if you were expecting to get the last
node you would need to use the path ROOT/Users/Users
RequestHandlerComponent• getReferrer is deprecated use getReferer
SessionComponent & SessionHelper• del is deprecated use delete
SessionComponent::setFlash() second param used to be used for setting the layout and accordingly rendered alayout file. This has been modified to use an element. If you specified custom session flash layouts in your applications
you will need to make the following changes.
1. Move the required layout files into app/views/elements
2. Rename the $content_for_layout variable to $message
3. Make sure you have echo $session->flash(); in your layout
SessionComponent and SessionHelper are not automatically loaded.
Both SessionComponent andSessionHelper are no longer automatically included without you asking for them. SessionHelper and SessionComponent now act like every other component and must be declared like any other helper/component. You should update
AppController::$components and AppController::$helpers to include these classes to retain existing behavior:

796

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

var $components = array('Session', 'Auth', ...);
var $helpers = array('Session', 'Html', 'Form' ...);
These change were done to make CakePHP more explicit and declarative in what classes you the application developer
want to use. In the past there was no way to avoid loading the Session classes without modifying core files. Which issomething we want you to be able to avoid. In addition Session classes were the only magical component and helper.
This change helps unify and normalize behavior amongst all classes.
Library Classes
CakeSession• del is deprecated use delete
SessionComponent• SessionComponent::setFlash() now uses an element instead of a layout as its second parameter. Be
sure to move any flash layouts from app/views/layouts to app/views/elements and change instances of $content_for_layout to $message.
Folder• mkdir is deprecated use create• mv is deprecated use move• ls is deprecated use read• cp is deprecated use copy
• rm is deprecated use delete
Set• isEqual is deprecated. Use == or ===.
String• getInstance is deprecated, call String methods statically.
RouterRouting.admin is deprecated. It provided an inconsistent behavior with other prefix style routes in that it was treateddifferently. Instead you should use Routing.prefixes. Prefix routes in 1.3 do not require additional routes to be
declared manually. All prefix routes will be generated automatically. To update simply change your core.php://from:Configure::write('Routing.admin', 'admin');//to:
Configure::write('Routing.prefixes', array('admin'));See the New features guide for more information on using prefix routes. A small change has also been done to routing
params. Routed params should now only consist of alphanumeric chars, - and _ or /[A-Z0-9-_+]+/:
Router::connect('/:$%@#param/:action/*', array(...)); // BAD
Router::connect('/:can/:anybody/:see/:m-3/*', array(...)); //Acceptable
For 1.3 the internals of the Router were heavily refactored to increase performance and reduce code clutter. The side
effect of this is two seldom used features were removed, as they were problematic and buggy even with the existingcode base. First path segments using full regular expressions was removed. You can no longer create routes like:
Migration from 1.2 to 1.3

797

CakePHP Cookbook Documentation, Release 2.x

Router::connect('/([0-9]+)-p-(.*)/',
array('controller' => 'products', 'action' => 'show'));
These routes complicated route compilation and impossible to reverse route. If you need routes like this, it is recommended that you use route parameters with capture patterns. Next mid-route greedy star support has been removed. It
was previously possible to use a greedy star in the middle of a route:
Router::connect('/pages/*/:event',array('controller' => 'pages', 'action' => 'display'),array('event' => '[a-z0-9_-]+'));
This is no longer supported as mid-route greedy stars behaved erratically, and complicated route compiling. Outsideof these two edge-case features and the above changes the router behaves exactly as it did in 1.2
Also, people using the ‘id’ key in array-form URLs will notice that Router::url() now treats this as a named parameter.
If you previously used this approach for passing the ID parameter to actions, you will need to rewrite all your $html>link() and $this->redirect() calls to reflect this change.
// old format:$url = array('controller' => 'posts', 'action' => 'view', 'id' => $id);// use cases:
Router::url($url);$html->link($url);$this->redirect($url);// 1.2 result:/posts/view/123// 1.3 result:/posts/view/id:123// correct format:$url = array('controller' => 'posts', 'action' => 'view', $id);
Dispatcher
Dispatcher is no longer capable of setting a controller’s layout/viewPath with request parameters. Control of theseproperties should be handled by the Controller, not the Dispatcher. This feature was also undocumented, and untested.
Debugger• Debugger::checkSessionKey() has been renamed to Debugger::checkSecurityKeys()• Calling Debugger::output("text") no longer works. Use Debugger::output("txt").
Object• Object::$_log has been removed. CakeLog::write is now called statically. See Logging for more information on changes made to logging.
Sanitize• Sanitize::html() now actually always returns escaped strings. In the past using the $remove parameter
would skip entity encoding, returning possibly dangerous content.
• Sanitize::clean() now has a remove_html option. This will trigger the strip_tags feature of
Sanitize::html(), and must be used in conjunction with the encode parameter.

798

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Configure and App• Configure::listObjects() replaced by App::objects()• Configure::corePaths() replaced by App::core()• Configure::buildPaths() replaced by App::build()• Configure no longer manages paths.
• Configure::write(‘modelPaths’, array. . . ) replaced by App::build(array(‘models’ => array. . . ))• Configure::read(‘modelPaths’) replaced by App::path(‘models’)• There is no longer a debug = 3. The controller dumps generated by this setting often caused memory consumption
issues making it an impractical and unusable setting. The $cakeDebug variable has also been removed from
View::renderLayout You should remove this variable reference to avoid errors.
• Configure::load() can now load configuration files from plugins. Use Configure::load('plugin.
file'); to load configuration files from plugins. Any configuration files in your application that use . in thename should be updated to use _Cache
In addition to being able to load CacheEngines from app/libs or plugins, Cache underwent some refactoring for
CakePHP1.3. These refactorings focused around reducing the number and frequency of method calls. The end result was a significant performance improvement with only a few minor API changes which are detailed below.
The changes in Cache removed the singletons used for each Engine type, and instead an engine instance is made foreach unique key created with Cache::config(). Since engines are not singletons anymore, Cache::engine() was
not needed and was removed. In addition Cache::isInitialized() now checks cache configuration names, notcache engine names. You can still use Cache::set() or Cache::engine() to modify cache configurations. Alsocheckout the New features in CakePHP 1.3 for more information on the additional methods added to Cache.
It should be noted that using an app/libs or plugin cache engine for the default cache config can cause performanceissues as the import that loads these classes will always be uncached. It is recommended that you either use one of thecore cache engines for your default configuration, or manually include the cache engine class before configuring it.
Furthermore any non-core cache engine configurations should be done in app/config/bootstrap.php for the same
reasons detailed above.
Model Databases and Datasources
Model• Model::del() and Model::remove() have been removed in favor of Model::delete(), which is now thecanonical delete method.
• Model::findAll, findCount, findNeighbours, removed.
• Dynamic calling of setTablePrefix() has been removed. tableprefix should be with the $tablePrefix property,
and any other custom construction behavior should be done in an overridden Model::__construct().
• DboSource::query() now throws warnings for un-handled model methods, instead of trying to run them as
queries. This means, people starting transactions improperly via the $this->Model->begin() syntax will need
to update their code so that it accesses the model’s DataSource object directly.
• Missing validation methods will now trigger errors in development mode.
• Missing behaviors will now trigger a cakeError.
• Model::find(first) will no longer use the id property for default conditions if no conditions are supplied andid is not empty. Instead no conditions will be used

Migration from 1.2 to 1.3

799

CakePHP Cookbook Documentation, Release 2.x• For Model::saveAll() the default value for option ‘validate’ is now ‘first’ instead of true
Datasources• DataSource::exists() has been refactored to be more consistent with non-database backed datasources. Previously, if you set var $useTable = false; var $useDbConfig = 'custom';, it was impossible forModel::exists() to return anything but false. This prevented custom datasources from using create()or update() correctly without some ugly hacks. If you have custom datasources that implement create(),update(), and read() (since Model::exists() will make a call to Model::find('count'), which is passed
to DataSource::read()), make sure to re-run your unit tests on 1.3.
Databases
Most database configurations no longer support the ‘connect’ key (which has been deprecated since pre-1.2). Instead,set 'persistent' => true or false to determine whether or not a persistent database connection should be used
SQL log dumping
A commonly asked question is how can one disable or remove the SQL log dump at the bottom of the page?. In
previous versions the HTML SQL log generation was buried inside DboSource. For 1.3 there is a new core element
called sql_dump. DboSource no longer automatically outputs SQL logs. If you want to output SQL logs in 1.3, do
the following:echo $this->element('sql_dump');You can place this element anywhere in your layout or view. The sql_dump element will only generate output when
Configure::read('debug') is equal to 2. You can of course customize or override this element in your app by
creating app/views/elements/sql_dump.ctp.
View and Helpers
View• View::renderElement removed. Use View::element() instead.
• Automagic support for .thtml view file extension has been removed either declare $this->ext = 'thtml';
in your controllers, or rename your views to use .ctp
• View::set('title', $var) no longer sets $title_for_layout when rendering the layout.
$title_for_layout is still populated by default.
But if you want to customize it, use$this->set('title_for_layout', $var).
• View::$pageTitle has been removed. Use $this->set('title_for_layout', $var); instead.
• The $cakeDebug layout variable associated with debug = 3 has been removed. Remove it from your layouts as
it will cause errors. Also see the notes related to SQL log dumping and Configure for more information.
All core helpers no longer use Helper::output(). The method was inconsistently used and caused output issues withmany of FormHelper’s methods. If you previously overrode AppHelper::output() to force helpers to auto-echo you
will need to update your view files to manually echo helper output.
TextHelper• TextHelper::trim() is deprecated, used truncate() instead.
• TextHelper::highlight() no longer has:
• an $highlighter parameter. Use $options['format'] instead.
• an $considerHtml``parameter. Use ``$options['html'] instead.
• TextHelper::truncate() no longer has:

800

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x• an $ending parameter. Use $options['ending'] instead.
• an $exact parameter. Use $options['exact'] instead.
• an $considerHtml``parameter. Use ``$options['html'] instead.
PaginatorHelper
PaginatorHelper has had a number of enhancements applied to make styling easier. prev(), next(), first() andlast()The disabled state of these methods now defaults to <span> tags instead of <div> tags.
passedArgs are now auto merged with URL options in paginator.
sort(), prev(), next() now add additional class names to the generated html. prev() adds a class of prev. next()
adds a class of next. sort() will add the direction currently being sorted, either asc or desc.
FormHelper• FormHelper::dateTime() no longer has a $showEmpty parameter. Use $attributes['empty'] instead.
• FormHelper::year() no longer has a $showEmpty parameter. Use $attributes['empty'] instead.
• FormHelper::month() no longer has a $showEmpty parameter. Use $attributes['empty'] instead.
• FormHelper::day() no longer has a $showEmpty parameter. Use $attributes['empty'] instead.
• FormHelper::minute() no longer has a $showEmpty parameter. Use $attributes['empty'] instead.
• FormHelper::meridian() no longer has a $showEmpty parameter. Use $attributes['empty'] instead.
• FormHelper::select() no longer has a $showEmpty parameter. Use $attributes['empty'] instead.
• Default URLs generated by form helper no longer contain ‘id’ parameter. This makes default URLs more consistent with documented userland routes. Also enables reverse routing to work in a more intuitive fashion withdefault FormHelper URLs.
• FormHelper::submit() Can now create other types of inputs other than type=submit. Use the type option tocontrol the type of input generated.
• FormHelper::button() Now creates <button> elements instead of reset or clear inputs. If you want to generate those types of inputs use FormHelper::submit() with a 'type' => 'reset' option for example.
• FormHelper::secure() and FormHelper::create() no longer create hidden fieldset elements. Instead theycreate hidden div elements. This improves validation with HTML4.
Also be sure to check the 2.0 updates for additional changes and new features in the FormHelper.
HtmlHelper• HtmlHelper::meta() no longer has an $inline parameter. It has been merged with the $options array.
• HtmlHelper::link() no longer has an $escapeTitle parameter. Use $options['escape'] instead. The
escape option now controls the escaping of the title and attributes at the same time.
• HtmlHelper::para() no longer has an $escape parameter. Use $options['escape'] instead.
• HtmlHelper::div() no longer has an $escape parameter. Use $options['escape'] instead.
• HtmlHelper::tag() no longer has an $escape parameter. Use $options['escape'] instead.
• HtmlHelper::css() no longer has an $inline parameter. Use $options['inline'] instead.
SessionHelper

Migration from 1.2 to 1.3

801CakePHP Cookbook Documentation, Release 2.x• flash() no longer auto echos. You must add an echo $session->flash(); to your session->flash() calls.
flash() was the only helper method that auto outputted, and was changed to create consistency in helper methods.
CacheHelper
CacheHelper’s interactions with Controller::$cacheAction has changed slightly. In the past if you used an array
for $cacheAction you were required to use the routed URL as the keys, this caused caching to break whenever routes
were changed. You also could set different cache durations for different passed argument values, but not different
named parameters or query string parameters. Both of these limitations/inconsistencies have been removed. You now
use the controller’s action names as the keys for $cacheAction. This makes configuring $cacheAction easier as it’s
no longer coupled to the routing, and allows cacheAction to work with all custom routing. If you need to have custom
cache durations for specific argument sets you will need to detect and update cacheAction in your controller.
TimeHelper
TimeHelper has been refactored to make it more i18n friendly. Internally almost all calls to date() have been replaced bystrftime(). The new method TimeHelper::i18nFormat() has been added and will take localization data from a LC_TIME
locale definition file in app/locale following the POSIX standard. These are the changes made in the TimeHelper API:• TimeHelper::format() can now take a time string as first parameter and a format string as the second one, theformat must be using the strftime() style. When called with this parameter order it will try to automaticallyconvert the date format into the preferred one for the current locale. It will also take parameters as in 1.2.x
version to be backwards compatible, but in this case format string must be compatible with date().
• TimeHelper::i18nFormat() has been added
Deprecated Helpers
Both the JavascriptHelper and the AjaxHelper are deprecated, and the JsHelper + HtmlHelper should be used in theirplace.
You should replace• $javascript->link() with $html->script()• $javascript->codeBlock() with $html->scriptBlock()$html->scriptEnd() depending on your usage.

or$html->scriptStart()

andConsole and shells
Shell
Shell::getAdmin() has been moved up to ProjectTask::getAdmin()
Schema shell• cake schema run create has been renamed to cake schema create• cake schema run update has been renamed to cake schema update
Console Error Handling
The shell dispatcher has been modified to exit with a 1 status code if the method called on the shell explicitly returns
false. Returning anything else results in a 0 status code. Before the value returned from the method was used directlyas the status code for exiting the shell.
Shell methods which are returning 1 to indicate an error should be updated to return false instead.
Shell::error() has been modified to exit with status code 1 after printing the error message which now uses aslightly different formatting.

802

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x$this->error('Invalid Foo', 'Please provide bar.');// outputs:
Error: Invalid Foo
Please provide bar.
// exits with status code 1
ShellDispatcher::stderr() has been modified to not prepend Error: to the message anymore. Its signature is now
similar to Shell::stdout().
ShellDispatcher::shiftArgs()
The method has been modified to return the shifted argument. Before if no arguments were available the method was
returning false, it now returns null. Before if arguments were available the method was returning true, it now returns
the shifted argument instead.
Vendors, Test Suite & schema
vendors/css, vendors/js, and vendors/img
Support for these three directories, both in app/vendors as well as plugin/vendors has been removed. They havebeen replaced with plugin and theme webroot directories.
Test Suite and Unit Tests
Group tests should now extend TestSuite instead of the deprecated GroupTest class. If your Group tests do not run, youwill need to update the base class.
Vendor, plugin and theme assets
Vendor asset serving has been removed in 1.3 in favour of plugin and theme webroot directories.
Schema files used with the SchemaShell have been moved to app/config/schema instead of app/config/sql Although config/sql will continue to work in 1.3, it will not in future versions, it is recommend that the new path isused.

New features in CakePHP 1.3
CakePHP 1.3 introduced a number of new features. This guide attempts to summarize those changes and point toexpanded documentation where necessary.
Components
SecurityComponent
The various requireXX methods like requireGet and requirePost now accept a single array as their argument aswell as a collection of string names.
$this->Security->requirePost(array('edit', 'update'));Component settings
Component settings for all core components can now be set from the $components array. Much like behaviors, you
can declare settings for components when you declare the component.

Migration from 1.2 to 1.3

803CakePHP Cookbook Documentation, Release 2.x

var $components = array('Cookie' => array('name' => 'MyCookie'),'Auth' => array('userModel' => 'MyUser','loginAction' => array('controller' => 'users', 'action' => 'login')));
This should reduce clutter in your Controller’s beforeFilter() methods.
EmailComponent• You can now retrieve the rendered contents of sent Email messages, by reading $this->Email->htmlMessage
and $this->Email->textMessage. These properties will contain the rendered email content matching theirname.
• Many of EmailComponent’s private methods have been made protected for easier extension.
• EmailComponent::$to can now be an array. Allowing easier setting of multiple recipients, and consistency withother properties.
• EmailComponent::$messageId has been added, it allows control over the Message-ID header for email messages.
View & Helpers
Helpers can now be addressed at $this->Helper->func() in addition to $helper->func(). This allows view
variables and helpers to share names and not create collisions.
New JsHelper and new features in HtmlHelper
See JsHelper documentation for more information
Pagination Helper
Pagination helper provides additional CSS classes for styling and you can set the default sort() direction.
PaginatorHelper::next() and PaginatorHelper::prev() now generate span tags by default, instead of divs.
HelperHelper::assetTimestamp() has been added. It will add timestamps to any asset under WWW_ROOT. It works with
Configure::read('Asset.timestamp'); just as before, but the functionality used in Html and Javascript helpers
has been made available to all helpers. Assuming Asset.timestamp == force$path = 'css/cake.generic.css'$stamped = $this->Html->assetTimestamp($path);//$stamped contains 'css/cake.generic.css?5632934892'
The appended timestamp contains the last modification time of the file. Since this method is defined in Helper it isavailable to all subclasses.
TextHelper
highlight() now accepts an array of words to highlight.
NumberHelper

804

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

A new method addFormat() has been added. This method allows you to set currency parameter sets, so you don’t
have to retype them:$this->Number->addFormat('NOK', array('before' => 'Kr. '));
$formatted = $this->Number->currency(1000, 'NOK');
FormHelper
The form helper has had a number of improvements and API modifications, see Form Helper improvements189 for more
information.
Logging
Logging and CakeLog have been enhanced considerably, both in features and flexibility. See New Logging features190
for more information.
Caching
Cache engines have been made more flexible in 1.3. You can now provide custom Cache adapters in app/libs aswell as in plugins using $plugin/libs. App/plugin cache engines can also override the core engines. Cache adaptersmust be in a cache directory. If you had a cache engine named MyCustomCacheEngine it would be placed in either
app/libs/cache/my_custom_cache.php as an app/libs. Or in $plugin/libs/cache/my_custom_cache.php
as part of a plugin. Cache configs from plugins need to use the plugin dot syntax:
Cache::config('custom', array('engine' => 'CachePack.MyCustomCache',...
));App and Plugin cache engines should be configured in app/bootstrap.php. If you try to configure them in core.php
they will not work correctly.
New Cache methods
Cache has a few new methods for 1.3 which make introspection and testing teardown easier.
• Cache::configured() returns an array of configured Cache engine keys.
• Cache::drop($config) drops a configured Cache engine. Once dropped cache engines are no longer readable
or writeable.
• Cache::increment() Perform an atomic increment on a numeric value. This is not implemented in FileEngine.
• Cache::decrement() Perform an atomic decrement on a numeric value.
FileEngine.

This is not implemented in

189 https://book.cakephp.org/1.3/en/The-Manual/Core-Helpers/Form.html#improvements

190 https://book.cakephp.org/1.3/en/The-Manual/Common-Tasks-With-CakePHP/Logging.html

Migration from 1.2 to 1.3805CakePHP Cookbook Documentation, Release 2.x

Models, Behaviors and Datasource
App::import(), datasources & datasources from plugins
Datasources can now be included loaded with App::import() and be included in plugins! To include a datasource inyour plugin you put it in my_plugin/models/datasources/your_datasource.php. To import a Datasource froma plugin use App::import('Datasource', 'MyPlugin.YourDatasource');
Using plugin datasources in your database.php
You can use plugin datasources by setting the datasource key with the plugin name. For example if you had a WebservicePack plugin with a LastFm datasource (plugin/webservice_pack/models/datasources/last_fm.php), you could
do:var $lastFm = array('datasource' => 'WebservicePack.LastFm'
...
Model
• Missing Validation methods now trigger errors, making debugging why validation isn’t working easier.
• Models now support virtual fields.
Behaviors
Using behaviors that do not exist, now triggers a cakeError making missing behaviors easier to find and fix.
CakeSchema
CakeSchema can now locate, read and write schema files to plugins. The SchemaShell also exposes this functionality,
see below for changes to SchemaShell. CakeSchema also supports tableParameters. Table Parameters are noncolumn specific table information such as collation, charset, comments, and table engine type. Each Dbo implements
the tableParameters they support.
tableParameters in MySQLMySQL supports the greatest number of tableParameters; You can use tableParameters to set a variety of MySQL
specific settings.
• engine Control the storage engine used for your tables.
• charset Control the character set used for tables.
• encoding Control the encoding used for tables.
In addition to tableParameters MySQL dbo’s implement fieldParameters. fieldParameters allow you to control
MySQL specific settings per column.
• charset Set the character set used for a column• encoding Set the encoding used for a column
See below for examples on how to use table and field parameters in your schema files.
tableParameters in Postgres

tableParameters in SQLite

Using tableParameters in schema files
You use tableParameters just as you would any other key in a schema file. Much like indexes:
806

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

var $comments => array('id' => array('type' => 'integer','null' => false,'default' => 0,'key' => 'primary'),'post_id' => array('type' => 'integer', 'null' => false, 'default' => 0),'comment' => array('type' => 'text'),'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true),'post_id' => array('column' => 'post_id'),),'tableParameters' => array('engine' => 'InnoDB','charset' => 'latin1','collate' => 'latin1_general_ci'));
is an example of a table using tableParameters to set some database specific settings. If you use a schema file that
contains options and features your database does not implement, those options will be ignored. For example if you
imported the above schema to a PostgreSQL server, all of the tableParameters would be ignore as PostgreSQL does notsupport any of the included options.
ConsoleBake
Bake has had a number of significant changes made to it. Those changes are detailed in the bake updates section191
Subclassing
The ShellDispatcher has been modified to not require shells and tasks to have Shell as their immediate parent anymore.
OutputShell::nl() has been added. It returns a single or multiple linefeed sequences. Shell::out(), err() and hr()
now accept a $newlines parameter which is passed to nl() and allows for controlling how newlines are appended tothe output.
Shell::out() and Shell::err() have been modified, allowing a parameterless usage. This is especially useful if
you’re often using $this->out('') for outputting just a single newline.
Acl Shell
All AclShell commands now take node parameters. node parameters can be either an alias path like controllers/
Posts/view or Model.foreign_key ie. User.1. You no longer need to know or use the aco/aro id for commands.
The Acl shell dataSource switch has been removed. Use the Configure settings instead.
SchemaShell
The Schema shell can now read and write Schema files and SQL dumps to plugins. It expects and will create schema
files in $plugin/config/schema

191 https://book.cakephp.org/1.3/en/The-Manual/Core-Helpers/Form.html#improvements

Migration from 1.2 to 1.3

807

CakePHP Cookbook Documentation, Release 2.x

Router and Dispatcher
Router
Generating URLs with new style prefixes works exactly the same as admin routing did in 1.2. They use thesame syntax and persist/behave in the same way. Assuming you have Configure::write('Routing.prefixes',
array('admin', 'member')); in your core.php you will be able to do the following from a non-prefixed URL:
$this->Html->link('Go',
array('controller' => 'posts', 'action' => 'index', 'member' => true));$this->Html->link('Go',
array('controller' => 'posts', 'action' => 'index', 'admin' => true));
Likewise, if you are in a prefixed URL and want to go to a non-prefixed URL, do the following:$this->Html->link('Go',
array('controller' => 'posts','action' => 'index','member' => false));$this->Html->link('Go',
array('controller' => 'posts','action' => 'index','admin' => false));
Route classes
For 1.3 the router has been internally rebuilt, and a new class CakeRoute has been created. This class handles theparsing and reverse matching of an individual connected route. Also new in 1.3 is the ability to create and use yourown Route classes. You can implement any special routing features that may be needed in application routing classes.
Developer route classes must extend CakeRoute, if they do not an error will be triggered. Commonly a custom routeclass will override the parse() and/or match() methods found in CakeRoute to provide custom handling.
Dispatcher• Accessing filtered asset paths, while having no defined asset filter will create 404 status code responses.

808

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

Library classes
Inflector
You can now globally customize the default transliteration map used in Inflector::slug using Inflector::rules. Example
Inflector::rules('transliteration', array('/å/' => 'aa', '/ø/' => 'oe'))
The Inflector now also internally caches all data passed to it for inflection (except slug method).
Set
Set has a new method Set::apply(), which allows you to apply callbacks192 to the results of Set::extract and do
so in either a map or reduce fashion:
Set::apply('/Movie/rating', $data, 'array_sum');
Would return the sum of all Movie ratings in $data.
L10N
All languages in the catalog now have a direction key. This can be used to determine/define the text direction of thelocale being used.
File• File now has a copy() method. It copies the file represented by the file instance, to a new location.
Configure• Configure::load() can now load configuration files from plugins. Use Configure::load('plugin.
file'); to load configuration files from plugins. Any configuration files in your application that use . in thename should be updated to used _App/libs
In addition to app/vendors a new app/libs directory has been added. This directory can also be part of plugins,located at $plugin/libs. Libs directories are intended to contain 1st party libraries that do not come from 3rdparties or external vendors. This allows you to separate your organization’s internal libraries from vendor libraries.
App::import() has also been updated to import from libs directories:
App::import('Lib', 'ImageManipulation'); //imports app/libs/image_manipulation.php
You can also import libs files from plugins:
App::import('Lib', 'Geocoding.Geocode'); //imports app/plugins/geocoding/libs/geocode.php
The remainder of lib importing syntax is identical to vendor files. So if you know how to import vendor files withunique names, you know how to import libs files with unique names.
Configuration• The default Security.level in 1.3 is medium instead of high
• There is a new configuration value Security.cipherSeed this value should be customized to ensure moresecure encrypted cookies, and a warning will be generated in development mode when the value matches its
default value.
i18n
Now you can use locale definition files for the LC_TIME category to retrieve date and time preferences for a specificlanguage. Just use any POSIX compliant locale definition file and store it at app/locale/language/ (do not create a folder
for the category LC_TIME, just put the file in there).
192 https://ca2.php.net/callback

Migration from 1.2 to 1.3

809CakePHP Cookbook Documentation, Release 2.x

For example, if you have access to a machine running debian or ubuntu you can find a french locale file at:/usr/share/i18n/locales/fr_FR. Copy the part corresponding to LC_TIME into app/locale/fr_fr/LC_TIME file. You
can then access the time preferences for French language this way:
Configure::write('Config.language','fr-fr'); // set the current language$monthNames = __c('mon',LC_TIME,true); // returns an array with the month names in French$dateFormat = __c('d_fmt',LC_TIME,true); // return the preferred dates format for France
You can read a complete guide of possible values in LC_TIME definition file in this page193
Miscellaneous
Error Handling
Subclasses of ErrorHandler can more easily implement additional error methods. In the past you would need to override__construct() and work around ErrorHandler’s desire to convert all error methods into error404 when debug = 0.
In 1.3, error methods that are declared in subclasses are not converted to error404. If you want your error methods
converted into error404, then you will need to do it manually.
Scaffolding
With the addition of Routing.prefixes scaffolding has been updated to allow the scaffolding of any one prefix:
Configure::write('Routing.prefixes', array('admin', 'member'));class PostsController extends AppController {var $scaffold = 'member';}
Would use scaffolding for member prefixed URLs.
Validation
After 1.2 was released, there were numerous requests to add additional localizations to the phone() and postal()methods. Instead of trying to add every locale to Validation itself, which would result in large bloated ugly methods,
and still not afford the flexibility needed for all cases, an alternate path was taken. In 1.3, phone() and postal() willpass off any country prefix it does not know how to handle to another class with the appropriate name. For example if
you lived in the Netherlands you would create a class like:class NlValidation {public function phone($check) {...
}public function postal($check) {...
}}This file could be placed anywhere in your application, but must be imported before attempting to use it. In your model
validation you could use your NlValidation class by doing the following:
public $validate = array('phone_no' => array('rule' => array('phone', null, 'nl')),'postal_code' => array('rule' => array('postal', null, 'nl')));
193 https://sunsson.iptime.org/susv3/basedefs/xbd_chap07.html

810

Chapter 14. Appendices

CakePHP Cookbook Documentation, Release 2.x

When your model data is validated, Validation will see that it cannot handle the ‘nl’ locale and will attempt to delegate
out to NlValidation::postal() and the return of that method will be used as the pass/fail for the validation. Thisapproach allows you to create classes that handle a subset or group of locales, something that a large switch would nothave. The usage of the individual validation methods has not changed, the ability to pass off to another validator has
been added.
IP Address Validation
Validation of IP Addresses has been extended to allow strict validation of a specific IP Version. It will also make useof PHP native validation mechanisms if available:
Validation::ip($someAddress);// Validates both IPv4 and IPv6
Validation::ip($someAddress, 'IPv4'); // Validates IPv4 Addresses only
Validation::ip($someAddress, 'IPv6'); // Validates IPv6 Addresses only
Validation::uuid()
A uuid() pattern validation has been added to the Validation class. It will check that a given string matches a UUID
by pattern only. It does not ensure uniqueness of the given UUID.

General Information
Glossary
routing array
An array of attributes that are passed to Router::url(). They typically look like:array('controller' => 'posts', 'action' => 'view', 5)Or a more complex example:
array('subdomain' => 'dev','plugin' => 'account','prefix' => 'admin','controller' => 'profiles','action' => 'edit',10257'#' => 'email','?' => array('reset' => true,),'full_base' => true,)
HTML attributes
An array of key => values that are composed into HTML attributes. For example:// Givenarray('class' => 'my-class', 'target' => '_blank')// Would generateclass="my-class" target="_blank"

General Information

811

CakePHP Cookbook Documentation, Release 2.x

If an option can be minimized or accepts it’s name as the value, then true can be used:// Givenarray('checked' => true)// Would generate
checked="checked"
plugin syntax
Plugin syntax refers to the dot separated class name indicating classes are part of a plugin. E.g. DebugKit.
Toolbar The plugin is DebugKit, and the class name is Toolbar.
dot notation
Dot notation defines an array path, by separating nested levels with . For example:
Asset.filter.css
Would point to the following value:
array('Asset' => array('filter' => array('css' => 'got me'
)
)
)
CSRF
Cross Site Request Forgery. Prevents replay attacks, double submissions and forged requests from other domains.
routes.php
A file in APP/Config that contains routing configuration. This file is included before each request is processed.
It should connect all the routes your application needs so requests can be routed to the correct controller + action.
DRY
Don’t repeat yourself. Is a principle of software development aimed at reducing repetition of information of all
kinds. In CakePHP DRY is used to allow you to code things once and re-use them across your application.

812

Chapter 14. AppendicesCHAPTER 15

Indices and tables• genindex813CakePHP Cookbook Documentation, Release 2.x

814

Chapter 15. Indices and tables

IndexSymbols:action, 589:controller, 589:plugin, 589$this->request, 55$this->response, 61__c() (global function), 334__construct() (Component method), 74__d() (global function), 334__dc() (global function), 334__dcn() (global function), 334__dn() (global function), 335__dx() (global function), 335__dxc() (global function), 335__dxcn() (global function), 336__dxn() (global function), 335__n() (global function), 336__x() (global function), 335__xc() (global function), 335__xn() (global function), 335A
acceptLanguage() (CakeRequest method), 61
accepts() (CakeRequest method), 61
accepts() (RequestHandlerComponent method), 389
AclBehavior (class), 280
AclComponent (class), 396
action (RssHelper property), 176
add() (Cache method), 416
add() (CacheEngine method), 413
addArgument() (ConsoleOptionParser method), 548
addArguments() (ConsoleOptionParser method), 548
addCrumb() (HtmlHelper method), 143
addDetector() (CakeRequest method), 60
addFormat() (CakeNumber method), 471
addFormat() (NumberHelper method), 158

addInputType() (RequestHandlerComponent method),391
addOption() (ConsoleOptionParser method), 549
addOptions() (ConsoleOptionParser method), 549
addPathElement() (Folder method), 425
addScript() (View method), 87
addSubcommand() (ConsoleOptionParser method), 550
admin routing, 594
afterDelete() (ModelBehavior method), 314
afterFilter() (Controller method), 46
afterFind() (ModelBehavior method), 313
afterLayout() (Helper method), 195
afterRender() (Helper method), 195
afterRenderFile() (Helper method), 195
afterSave() (ModelBehavior method), 314
afterScaffoldSave() (Controller method), 49
afterScaffoldSaveError() (Controller method), 49
afterValidate() (ModelBehavior method), 313
ajaxLogin (AuthComponent property), 381
alert() (CakeLog method), 469
alert() (JsHelper method), 153
allow() (AuthComponent method), 382
allowedActions (AuthComponent property), 381
allowedActions (SecurityComponent property), 385
allowedControllers (SecurityComponent property),
385
allowMethod() (CakeRequest method), 60
alphaNumeric() (Validation method), 264
am() (global function), 336
App (class), 339
APP (global constant), 337
APP_DIR (global constant), 337
append() (File method), 431
append() (View method), 88
appError, 617
APPLIBS (global constant), 337
application exceptions, 615
apply() (Hash method), 446
815

CakePHP Cookbook Documentation, Release 2.x

apply() (Set method), 480
AppShell (class), 553
assign() (View method), 88
attachments() (CakeEmail method), 422
AuthComponent (class), 367
authenticate (AuthComponent property), 381
authError (AuthComponent property), 381
authorize (AuthComponent property), 381
autoLink() (TextHelper method), 180
autoLinkEmails() (TextHelper method), 179
autoLinkUrls() (TextHelper method), 179
autoParagraph() (TextHelper method), 180

CakeEmail (class), 417
CakeException, 613
CakeLog (class), 467
CakeNumber (class), 469
CakeRequest (class), 59
CakeResponse (class), 67
CakeRoute (class), 605
CakeSession (class), 611
CakeText (class), 509
CakeTime (class), 514
camelize() (Inflector method), 458
cc() (Validation method), 265
cd() (Folder method), 426
Bchannel() (RssHelper method), 176
charset() (CakeResponse method), 67
BadRequestException, 613
charset() (HtmlHelper method), 126base (CakeRequest property), 61
check() (Configure method), 583
base (RssHelper property), 176
check() (CookieComponent method), 396
beforeDelete() (ModelBehavior method), 314
check() (Hash method), 442
beforeFilter() (Controller method), 45
check() (SessionComponent method), 365
beforeFind() (ModelBehavior method), 313
check() (SessionHelper method), 177
beforeLayout() (Helper method), 195
check() (Set method), 480
beforeRedirect() (Component method), 75
checkbox() (FormHelper method), 115
beforeRender() (Component method), 75
checkNotModified() (CakeResponse method), 67
beforeRender() (Controller method), 46
childCount() (TreeBehavior method), 302
beforeRender() (Helper method), 195
children() (TreeBehavior method), 302
beforeRenderFile() (Helper method), 195
chmod() (Folder method), 426
beforeSave() (ModelBehavior method), 313
cipher() (Security method), 477
beforeScaffold() (Controller method), 49
classicExtract() (Set method), 481
beforeValidate() (ModelBehavior method), 313
classify() (Inflector method), 458
bindTranslation() (TranslateBehavior method), 293
cleanInsert() (CakeText method), 509
blackHole() (SecurityComponent method), 384
blackHoleCallback (SecurityComponent property), cleanup() (ModelBehavior method), 313
clear() (Cache method), 416
384
clear() (CacheEngine method), 412
blank() (Validation method), 264
clear() (Shell method), 553
Blocks (View property), 88
clearGroup() (Cache method), 416
blocks() (View method), 87
clearGroup() (CacheEngine method), 412
body() (CakeResponse method), 68
clientIp() (CakeRequest method), 60
boolean() (Validation method), 264
close() (File method), 431
buffer() (JsHelper method), 147
combine() (Hash method), 437
build() (App method), 342
combine() (Set method), 484
build() (Xml method), 526
buildFromArray() (ConsoleOptionParser method), 551 comparison() (Validation method), 265
compile() (CakeRoute method), 605
button() (FormHelper method), 121
Component (class), 74
components (AuthComponent property), 381
Ccomponents (Controller property), 54
Cache (class), 415
compress() (CakeResponse method), 67
CACHE (global constant), 337
CONFIG (global constant), 338
cache() (CakeResponse method), 67
config()(Cache method), 415
cacheAction (Controller property), 55
config()(CakeLog
method), 467
CacheEngine (class), 412
config()(Configure
method), 583
CacheHelper (class), 94
config()(globalfunction),
336
CAKE (global constant), 337
ConfigReaderInterface(interface),
586
CAKE_CORE_INCLUDE_PATH (global constant), 337
configuration,
578
CakeBaseException, 613

816

Index

CakePHP Cookbook Documentation, Release 2.x

Configure (class), 581
configured() (CakeLog method), 467
configured() (Configure method), 583
ConfigureException, 587
confirm() (JsHelper method), 153
connect() (Router method), 603
connectNamed() (Router method), 604
ConsoleOptionParser (class), 546
constructAuthenticate() (AuthComponent method),382
constructAuthorize() (AuthComponent method), 382
constructClasses() (Controller method), 49
consume() (Configure method), 582
consume() (SessionComponent method), 365
consume() (SessionHelper method), 177
ContainableBehavior (class), 282
contains() (Hash method), 441
contains() (Set method), 488
Controller (class), 45
convert() (CakeTime method), 514
convert() (TimeHelper method), 185convertSlash() (global function), 336
convertSpecifiers() (CakeTime method), 514
convertSpecifiers() (TimeHelper method), 185
CookieComponent (class), 394
copy() (File method), 431
copy() (Folder method), 426
core() (App method), 341
core.php, 578
CORE_PATH (global constant), 338
correctSlashFor() (Folder method), 427
countDim() (Set method), 489
counter() (PaginatorHelper method), 167
create() (File method), 432
create() (Folder method), 427
create() (FormHelper method), 97
createFile() (Shell method), 553
critical() (CakeLog method), 469
CSRF, 812
csrfCheck (SecurityComponent property), 386
csrfExpires (SecurityComponent property), 386
csrfUseOnce (SecurityComponent property), 386
CSS (global constant), 338
css() (HtmlHelper method), 126
CSS_URL (global constant), 338
currency() (CakeNumber method), 470
currency() (NumberHelper method), 157
current() (PaginatorHelper method), 167
custom() (Validation method), 266

Ddata (CakeRequest property), 61
data (RssHelper property), 176data() (CakeRequest method), 60
Index

database.php, 575
database.php.default, 575
date() (Validation method), 266
dateTime() (FormHelper method), 122datetime() (Validation method), 267
DAY (global constant), 339
day() (FormHelper method), 123
dayAsSql() (CakeTime method), 514
dayAsSql() (TimeHelper method), 185
daysAsSql() (CakeTime method), 515
daysAsSql() (TimeHelper method), 185
debug() (CakeLog method), 469
debug() (global function), 336
Debugger (class), 621
decimal() (Validation method), 267
decrement() (Cache method), 416
decrement() (CacheEngine method), 413
decrypt() (Security method), 478
defaultCurrency() (CakeNumber method), 471
defaultCurrency() (NumberHelper method), 158
defaultLevels() (CakeLog method), 468
defaultModel() (PaginatorHelper method), 172
defaultRouteClass() (Router method), 605
delete() (Cache method), 416
delete() (CacheEngine method), 412
delete() (CakeSession method), 611
delete() (Configure method), 583
delete() (CookieComponent method), 396
delete() (File method), 432
delete() (Folder method), 427
delete() (HttpSocket method), 452
delete() (SessionComponent method), 365deny() (AuthComponent method), 382
description() (ConsoleOptionParser method), 547
destroy() (CookieComponent method), 396
destroy() (SessionComponent method), 366
diff() (Hash method), 447
diff() (Set method), 490
dimensions() (Hash method), 445
dirsize() (Folder method), 427
disable() (CakeLog method), 469
disableCache() (CakeResponse method), 67
disableCache() (Controller method), 50
dispatchShell() (Shell method), 554
div() (HtmlHelper method), 134
doc (role), 695
docType() (HtmlHelper method), 129
document() (RssHelper method), 176
domain() (CakeRequest method), 59
domId() (Helper method), 195
domReady() (JsHelper method), 153
dot notation, 812
download() (CakeResponse method), 68
drag() (JsHelper method), 149817

CakePHP Cookbook Documentation, Release 2.x

drop() (CakeLog method), 467
drop() (Configure method), 583
drop() (JsHelper method), 150
DRY, 812
DS (global constant), 338
dump() (ConfigReaderInterface method), 586
dump() (Configure method), 584
dump() (Debugger method), 621

E
each() (JsHelper method), 153
effect() (JsHelper method), 151elem() (RssHelper method), 176
element() (View method), 87
elementCache (View property), 88
email() (Validation method), 267
emailPattern() (CakeEmail method), 424
emergency() (CakeLog method), 469
enable() (CakeLog method), 468
enabled() (CakeLog method), 468
encrypt() (Security method), 477
end() (FormHelper method), 100
end() (View method), 88
enum() (Set method), 491
env() (global function), 337
epilog() (ConsoleOptionParser method), 547
equalTo() (Validation method), 268
err() (Shell method), 554
error() (CakeLog method), 469
error() (FormHelper method), 124error() (SessionHelper method), 177error() (Shell method), 554
errors() (Folder method), 427
etag() (CakeResponse method), 67
event() (JsHelper method), 152
ExceptionRenderer (class), 615
excerpt() (CakeText method), 513
excerpt() (Debugger method), 622
excerpt() (TextHelper method), 183
executable() (File method), 432
exists() (File method), 432
expand() (Hash method), 443
expires() (CakeResponse method), 67
exportVar() (Debugger method), 623
ext() (File method), 432
extend() (View method), 88
extension() (Validation method), 268
extract() (Hash method), 436
extract() (Set method), 492

Ffetch() (View method), 88
field (RssHelper property), 176
File (class), 431
818

file extensions, 595
file() (CakeResponse method), 68
file() (FormHelper method), 119
fileExistsInPath() (global function), 337
fileSize() (Validation method), 268
filter() (Hash method), 442
filter() (Set method), 493
find() (Folder method), 428
findRecursive() (Folder method), 428
first() (PaginatorHelper method), 166
flash (AuthComponent property), 381
flash() (Controller method), 49
flash() (SessionHelper method), 178
FlashComponent (class), 363
FlashHelper (class), 96
flatten() (Hash method), 443
flatten() (Set method), 493
Folder (class), 425
Folder (File property), 431
Folder() (File method), 432
ForbiddenException, 613
format() (CakeNumber method), 474
format() (CakeTime method), 515
format() (Hash method), 440
format() (NumberHelper method), 161
format() (Set method), 494
format() (TimeHelper method), 186
formatDelta() (CakeNumber method), 475
formatDelta() (NumberHelper method), 162
formatTreeList() (TreeBehavior method), 303
FormHelper (class), 97
fromReadableSize() (CakeNumber method), 473
fromReadableSize() (NumberHelper method), 160
fromString() (CakeTime method), 516
fromString() (TimeHelper method), 186
FULL_BASE_URL (global constant), 338
fullBaseUrl() (Router method), 605

G
gc() (Cache method), 417
gc() (CacheEngine method), 413
generateTreeList() (TreeBehavior method), 303
get() (Hash method), 436
get() (HttpSocket method), 451
get() (JsHelper method), 149
get() (View method), 87
getAjaxVersion()(RequestHandlerComponent
method), 391
getBuffer() (JsHelper method), 147
getCrumbList() (HtmlHelper method), 143
getCrumbs() (HtmlHelper method), 142
getLevel() (TreeBehavior method), 309
getOptionParser() (Shell method), 554
getParentNode() (TreeBehavior method), 304
Index

CakePHP Cookbook Documentation, Release 2.x

getPath() (TreeBehavior method), 304
getType() (Debugger method), 623
getVar() (View method), 87
getVars() (View method), 87
gmt() (CakeTime method), 516
gmt() (TimeHelper method), 187
group() (File method), 432
groupConfigs() (Cache method), 417

Hh() (global function), 337
handle (File property), 431
Hash (class), 435
hash() (Security method), 478
hasMethod() (Shell method), 554
hasNext() (PaginatorHelper method), 167
hasPage() (PaginatorHelper method), 167
hasPrev() (PaginatorHelper method), 167
hasTask() (Shell method), 554
header() (CakeRequest method), 60
header() (CakeResponse method), 67
Helper (class), 195
helpers (Controller property), 54
helpers (RssHelper property), 176
here (CakeRequest property), 61
here (RssHelper property), 176
hidden() (FormHelper method), 114
highlight() (CakeText method), 510
highlight() (TextHelper method), 180host() (CakeRequest method), 59
HOUR (global constant), 339
hour() (FormHelper method), 123
hr() (Shell method), 554
HTML attributes, 811
HtmlHelper (class), 126
HttpSocket (class), 451
humanize() (Inflector method), 458

I
i18nFormat() (CakeTime method), 516
i18nFormat() (TimeHelper method), 187
identify() (AuthComponent method), 382
image() (HtmlHelper method), 130
IMAGES (global constant), 338
IMAGES_URL (global constant), 338
import() (App method), 344in() (Shell method), 554
inCakePath() (Folder method), 428
increment() (Cache method), 416
increment() (CacheEngine method), 413
Inflector (class), 457
info (File property), 431
info() (CakeLog method), 469
info() (File method), 432
Index

IniReader (class), 587
init() (App method), 346
initialize() (AuthComponent method), 382
initialize() (Component method), 75
initialize() (Shell method), 555
inList() (Validation method), 268
inPath() (Folder method), 429
input() (CakeRequest method), 60
input() (FormHelper method), 101
inputs() (FormHelper method), 103
insert() (CakeText method), 509
insert() (Hash method), 436
insert() (Set method), 495
InternalErrorException, 613
invoke() (Debugger method), 623
ip() (Validation method), 269
is() (CakeRequest method), 60
isAbsolute() (Folder method), 429
isAtom() (RequestHandlerComponent method), 389
isAuthorized() (AuthComponent method), 382
isFieldError() (FormHelper method), 124
isFuture() (CakeTime method), 520
isFuture() (TimeHelper method), 191
isMobile() (RequestHandlerComponent method), 389
isPast() (CakeTime method), 520
isPast() (TimeHelper method), 191
isRss() (RequestHandlerComponent method), 389
isSlashTerm() (Folder method), 429
isThisMonth() (CakeTime method), 520
isThisMonth() (TimeHelper method), 191
isThisWeek() (CakeTime method), 520
isThisWeek() (TimeHelper method), 191
isThisYear() (CakeTime method), 520
isThisYear() (TimeHelper method), 191
isToday() (CakeTime method), 520
isToday() (TimeHelper method), 191
isTomorrow() (CakeTime method), 520
isTomorrow() (TimeHelper method), 191
isWap() (RequestHandlerComponent method), 390
isWindowsPath() (Folder method), 429
isXml() (RequestHandlerComponent method), 389
item() (RssHelper method), 176
items() (RssHelper method), 177

JJS (global constant), 338
JS_URL (global constant), 338
JsHelper (class), 144
JsonView (class), 93

Llabel() (FormHelper method), 113
last() (PaginatorHelper method), 167
lastAccess() (File method), 432
819

CakePHP Cookbook Documentation, Release 2.x

lastChange() (File method), 432
layout (View property), 88
lengthBetween() (Validation method), 264
levels() (CakeLog method), 467
link() (HtmlHelper method), 131link() (JsHelper method), 154link() (PaginatorHelper method), 171
listTimezones() (CakeTime method), 520
listTimezones() (TimeHelper method), 190load() (App method), 346
load() (Configure method), 584
loadConfig() (HtmlHelper method), 142
loadModel() (Controller method), 53
loadTasks() (Shell method), 555
location() (App method), 341
location() (CakeResponse method), 67
lock (File property), 431
log() (Debugger method), 622
LOG_ERROR (global constant), 581
LogError() (global function), 337
loggedIn() (AuthComponent method), 382
login() (AuthComponent method), 382
loginAction (AuthComponent property), 381
loginRedirect (AuthComponent property), 381
logout() (AuthComponent method), 382
logoutRedirect (AuthComponent property), 381
LOGS (global constant), 338
luhn() (Validation method), 269

minLengthBytes() (Validation method), 270
MINUTE (global constant), 339
minute() (FormHelper method), 123
MissingActionException, 614
MissingBehaviorException, 614
MissingComponentException, 614
MissingConnectionException, 614
MissingControllerException, 614
MissingDatabaseException, 614
MissingHelperException, 614
MissingLayoutException, 614
MissingShellException, 614
MissingShellMethodException, 614
MissingTableException, 614
MissingTaskException, 614
MissingViewException, 613
mode (Folder property), 425
model (RssHelper property), 176
ModelBehavior (class), 313
modified() (CakeResponse method), 67
money() (Validation method), 271
MONTH (global constant), 339
month() (FormHelper method), 122move() (Folder method), 429
moveDown() (TreeBehavior method), 305
moveUp() (TreeBehavior method), 305
multiple() (Validation method), 271

Mname (Controller property), 53
name (File property), 431
name() (File method), 433
named parameters, 597
naturalNumber() (Validation method), 272
nest() (Hash method), 450
nest() (Set method), 500
nestedList() (HtmlHelper method), 137
next() (PaginatorHelper method), 166
nice() (CakeTime method), 517
nice() (TimeHelper method), 187
niceShort() (CakeTime method), 517
niceShort() (TimeHelper method), 188nl() (Shell method), 555
normalize() (Hash method), 449
normalize() (Set method), 501
normalizePath() (Folder method), 430
notBlank() (Validation method), 271
notEmpty() (Validation method), 271
NotFoundException, 613
notice() (CakeLog method), 469
NotImplementedException, 613
NumberHelper (class), 157
numbers() (PaginatorHelper method), 164
numeric() (Hash method), 445

map() (Hash method), 446
map() (Set method), 496
mapActions() (AuthComponent method), 382
mapResources() (Router method), 605
match() (CakeRoute method), 605
matches() (Set method), 498
maxDimensions() (Hash method), 446
maxLength() (Validation method), 269
maxLengthBytes() (Validation method), 270
md5() (File method), 433
media() (HtmlHelper method), 133
MediaView (class), 90
merge() (Hash method), 444
merge() (Set method), 499
mergeDiff() (Hash method), 448
meridian() (FormHelper method), 123
messages() (Folder method), 429
meta() (HtmlHelper method), 127meta() (PaginatorHelper method), 172
method() (CakeRequest method), 59
MethodNotAllowedException, 613
mime() (File method), 434
mimeType() (Validation method), 270
minLength() (Validation method), 270
820

N

Index

CakePHP Cookbook Documentation, Release 2.x

numeric() (Set method), 503
numeric() (Validation method), 271

O
object() (JsHelper method), 147
objects() (App method), 343
offset() (File method), 433
onlyAllow() (CakeRequest method), 59
open() (File method), 433
options() (PaginatorHelper method), 168
out() (Shell method), 555
output (View property), 88
overwrite() (Shell method), 555
owner() (File method), 433

P
paginate (Controller property), 55
paginate() (Controller method), 51
PaginatorComponent (class), 357
PaginatorHelper (class), 163para() (HtmlHelper method), 135
param() (CakeRequest method), 61
param() (PaginatorHelper method), 172
param() (Shell method), 553
params (CakeRequest property), 61
params (RssHelper property), 176
params() (PaginatorHelper method), 172
parse() (CakeRoute method), 605
parseExtensions() (Router method), 605
passed arguments, 596
password() (AuthComponent method), 382
password() (FormHelper method), 113
patch() (HttpSocket method), 452
path (File property), 431
path (Folder property), 425
path() (App method), 341
paths() (App method), 341
perms() (File method), 433
phone() (Validation method), 272
php:attr (directive), 697
php:attr (role), 698
php:class (directive), 696
php:class (role), 697
php:const (directive), 696
php:const (role), 697
php:exc (role), 698
php:exception (directive), 696
php:func (role), 697
php:function (directive), 696
php:global (directive), 696
php:global (role), 697
php:meth (role), 697
php:method (directive), 697
php:staticmethod (directive), 697
Index

PhpReader (class), 587
plugin routing, 595
plugin syntax, 812
pluginPath() (App method), 343
pluginSplit() (global function), 337
pluralize() (Inflector method), 457
post() (HttpSocket method), 451
postal() (Validation method), 272
postButton() (FormHelper method), 121
postConditions() (Controller method), 50
postLink() (FormHelper method), 122pr() (global function), 337
precision() (CakeNumber method), 472
precision() (NumberHelper method), 159prefers() (RequestHandlerComponent method), 392
prefix routing, 594
prepare() (File method), 433
prepend() (View method), 88
prev() (PaginatorHelper method), 165
PrivateActionException, 614
promote() (Router method), 604
prompt() (JsHelper method), 153
pushDiff() (Set method), 504
put() (HttpSocket method), 452
pwd() (File method), 433
pwd() (Folder method), 430

Qquery (CakeRequest property), 61
query() (CakeRequest method), 60

Rradio() (FormHelper method), 115
range() (Validation method), 272
read() (Cache method), 415
read() (CacheEngine method), 412
read() (CakeSession method), 611
read() (ConfigReaderInterface method), 586
read() (Configure method), 582
read() (CookieComponent method), 395
read() (File method), 433
read() (Folder method), 430read() (SessionComponent method), 365read() (SessionHelper method), 177readable() (File method), 434
realpath() (Folder method), 430
recover() (TreeBehavior method), 307
redirect() (AuthComponent method), 383
redirect() (Controller method), 48
redirect() (JsHelper method), 155
redirect() (Router method), 604
redirectUrl() (AuthComponent method), 383
reduce() (Hash method), 446
ref (role), 695
821

CakePHP Cookbook Documentation, Release 2.x

referer() (CakeRequest method), 60
referer() (Controller method), 49
remember() (Cache method), 417
remove() (Hash method), 437
remove() (Set method), 505
removeFromTree() (TreeBehavior method), 306
render() (Controller method), 47
renderAs() (RequestHandlerComponent method), 392
reorder() (TreeBehavior method), 306, 307
replaceText() (File method), 434
request (AuthComponent property), 381
request (View property), 88
request() (HttpSocket method), 452
request() (JsHelper method), 148
requestAction() (Controller method), 51
RequestHandlerComponent (class), 388
requireAuth() (SecurityComponent method), 385
requireDelete() (SecurityComponent method), 384
requireGet() (SecurityComponent method), 384
requirePost() (SecurityComponent method), 384
requirePut() (SecurityComponent method), 384
requireSecure() (SecurityComponent method), 385
reset() (Inflector method), 458
respondAs() (RequestHandlerComponent method), 392
response (AuthComponent property), 381
responseHeader() (CakeBaseException method), 613
responseType() (RequestHandlerComponent method),393
restore() (Configure method), 585
reverse() (Set method), 506
RFC
RFC 2606, 713
RFC 2616, 454, 773
RFC 4122, 509
rijndael() (Security method), 477
ROOT (global constant), 338
Router (class), 603
routes.php, 589, 812
routing array, 811
RssHelper (class), 173
rules() (Inflector method), 459
runCommand() (Shell method), 556

S
safe() (File method), 434
scaffoldError() (Controller method), 49
script() (HtmlHelper method), 135
scriptBlock() (HtmlHelper method), 136
scriptEnd() (HtmlHelper method), 137
scriptStart() (HtmlHelper method), 136
SECOND (global constant), 339
secure() (FormHelper method), 125Security (class), 476
SecurityComponent (class), 383822

select() (FormHelper method), 116send() (CakeResponse method), 68
serializeForm() (JsHelper method), 154
serverOffset() (CakeTime method), 517
serverOffset() (TimeHelper method), 188
SessionComponent (class), 365
SessionHelper (class), 177sessionKey (AuthComponent property), 381
Set (class), 479
set() (Cache method), 416
set() (Controller method), 46
set() (JsHelper method), 149
set() (View method), 87
setContent() (RequestHandlerComponent method),391
setExtensions() (Router method), 605
setFlash() (SessionComponent method), 366
setHash() (Security method), 479
setup() (ModelBehavior method), 313
sharable() (CakeResponse method), 67
Shell (class), 553
shortPath() (Shell method), 556
shutdown() (App method), 346
shutdown() (AuthComponent method), 383
shutdown() (Component method), 75
singularize() (Inflector method), 458
size() (File method), 434
slashTerm() (Folder method), 430
slider() (JsHelper method), 151slug() (Inflector method), 458
sort (Folder property), 425
sort() (Hash method), 446
sort() (PaginatorHelper method), 163sort() (Set method), 508
sortable() (JsHelper method), 148
sortByKey() (global function), 337
sortDir() (PaginatorHelper method), 164
sortKey() (PaginatorHelper method), 164
ssn() (Validation method), 273
start() (View method), 87
startIfEmpty() (View method), 88
startup() (AuthComponent method), 383
startup() (Component method), 75
startup() (Shell method), 556
statusCode() (CakeResponse method), 68
store() (Configure method), 585
stream() (CakeLog method), 469
stripLinks() (CakeText method), 511
stripLinks() (TextHelper method), 181
stripslashes_deep() (global function), 337
style() (HtmlHelper method), 129
subdomains() (CakeRequest method), 59
submit() (FormHelper method), 121
submit() (JsHelper method), 153
Index

CakePHP Cookbook Documentation, Release 2.x

TtableCells() (HtmlHelper method), 139
tableHeaders() (HtmlHelper method), 138
tableize() (Inflector method), 458
tag() (HtmlHelper method), 134
tagIsInvalid() (FormHelper method), 124tail() (CakeText method), 512
tail() (TextHelper method), 182
tasks (Shell property), 553
TESTS (global constant), 338
text() (FormHelper method), 113
textarea() (FormHelper method), 114
TextHelper (class), 179
themePath() (App method), 343
time() (RssHelper method), 177time() (Validation method), 273
TIME_START (global constant), 339
timeAgoInWords() (CakeTime method), 517
timeAgoInWords() (TimeHelper method), 188
TimeHelper (class), 184timezone() (CakeTime method), 519
timezone() (TimeHelper method), 190
TMP (global constant), 338
toArray() (Xml method), 527
toAtom() (CakeTime method), 518
toAtom() (TimeHelper method), 189
tokenize() (CakeText method), 509
toList() (CakeText method), 513
toList() (TextHelper method), 183
toPercentage() (CakeNumber method), 472
toPercentage() (NumberHelper method), 159
toQuarter() (CakeTime method), 518
toQuarter() (TimeHelper method), 189
toReadableSize() (CakeNumber method), 473
toReadableSize() (NumberHelper method), 160
toRSS() (CakeTime method), 519
toRSS() (TimeHelper method), 190
toServer() (CakeTime method), 519
toServer() (TimeHelper method), 190
toUnix() (CakeTime method), 519
toUnix() (TimeHelper method), 190
trace() (Debugger method), 622
TranslateBehavior (class), 289
tree() (Folder method), 431
TreeBehavior (class), 296, 302
truncate() (CakeText method), 511
truncate() (TextHelper method), 181
type() (CakeResponse method), 67
type() (CookieComponent method), 396

U
UnauthorizedException, 613
unauthorizedRedirect (AuthComponent property),
381
Index

underscore() (Inflector method), 458
unlockedFields (SecurityComponent property), 385
unlockField() (FormHelper method), 125
uploadError() (Validation method), 273
url() (Helper method), 195
url() (HtmlHelper method), 140
url() (PaginatorHelper method), 171
url() (Router method), 604
url() (Validation method), 273
user() (AuthComponent method), 383
userDefined() (Validation method), 274
uses (Controller property), 54
uses (Shell property), 553
uses() (App method), 340
useTag() (HtmlHelper method), 142uuid() (CakeText method), 509
uuid() (Validation method), 274
uuid() (View method), 87

Vvalid() (SessionHelper method), 178
validatePost (SecurityComponent property), 385
Validation (class), 264
ValidationisUnique() (ModelValidation method),269
value() (Helper method), 195
value() (JsHelper method), 155variable() (Inflector method), 458
VENDORS (global constant), 338
verify() (TreeBehavior method), 308
version (RssHelper property), 176version() (Configure method), 583
View (class), 87
viewClassMap() (RequestHandlerComponent method),393

W
warning() (CakeLog method), 469
wasWithinLast() (CakeTime method), 520
wasWithinLast() (TimeHelper method), 191
wasYesterday() (CakeTime method), 520
wasYesterday() (TimeHelper method), 191
webroot (CakeRequest property), 61
webroot() (Helper method), 195
WEBROOT_DIR (global constant), 339
WEEK (global constant), 339
wrap() (CakeText method), 510
wrapText() (Shell method), 556
writable() (File method), 434
write() (Cache method), 416
write() (CacheEngine method), 412
write() (CakeLog method), 467
write() (CakeSession method), 611
write() (Configure method), 582
823

CakePHP Cookbook Documentation, Release 2.x

write() (CookieComponent method), 395
write() (File method), 434
write() (SessionComponent method), 365
writeBuffer() (JsHelper method), 146X
Xml (class), 521
XmlView (class), 93

Y
YEAR (global constant), 339
year() (FormHelper method), 122824

Index