Cakephp
Cakephp
2
The Good Stuff
CakePHP v1.2
1
Beginning With CakePHP
7
Preface
7
Audience
7
Introduction to CakePHP
7
What is CakePHP? Why Use it?
7
Understanding Model-View-Contro%er
10
Overview
10
Benefits
12
CakePHP v1.2
2
CakePHP Structure
13
Contro%er Aids
13
View Aids
13
Model Aids
14
Application Aids
15
A Typical CakePHP
Request
18
CakePHP Conventions
19
File and Classname Conventions
19
Model Conventions
20
Contro%er Conventions
20
View Conventions
21
CakePHP v1.2
3
Developing with CakePHP
22
Requirements
22
Insta%ation Preparation
22
Getting CakePHP
22
Permissions
23
Insta%ation
23
Development
23
Production
24
Advanced Insta%ation
24
Fire It Up
27
Configuration
27
Database Configuration
27
Core Configuration
28
CakePHP v1.2
4
Routes Configuration
30
Custom Inflections
34
Bootstrapping CakePHP
35
Contro%ers
36
Introduction
36
Contro%er Attributes
37
$name
37
Other Attributes
41
Contro%er Methods
41
Interacting with Views
41
Flow Control
42
Callbacks
43
Components
48
Introduction
48
CakePHP v1.2
5
Models
50
Introduction
50
Model Attributes
51
Model Methods
54
Retrieving Your Data
54
Model Callbacks
63
User-Defined Functions
65
Associations
65
Introduction
65
hasOne
66
belongsTo
68
hasMany
70
hasAndBelongsToMany (HABTM)
73
DataSources
78
Behaviors
78
CakePHP v1.2
6
B E G I N N I N G WI TH CAK EP HP
Patty-cake, patty-cake...
Preface
Welcome to web development heaven.
If you’re reading the preface to a technical manual, you probably have too much time
on your hands. I’m no celebrity, and the material is what you’re after, so skip this
superfluous section and dive right in.
AUDIENCE
In order to read this manual, you’re going to need to be moderately familiar with
PHP. Some familiarity with object-oriented programming will help, though I suppose
the introductory sections of this manual could act as a primer of sorts. That said, this
material is written to developers of all levels of ability–anyone who wants to create
robust, maintainable applications quickly and enjoyably.
I should say that there will be sections that address technologies that are really out of
the scope of this manual. Web server administration, AJAX and JavaScript details
and the like may be touched on in places, but for the most part we’re just going to
stick to CakePHP.
Introduction to CakePHP
W H AT I S C A K E P H P ? W H Y U S E I T ?
CakePHP is a free, open-source, rapid development framework for PHP. It’s a
foundational structure for programmers 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. We provide you with all the
tools you need to get started coding what you rea%y need to get done: the logic
specific to your application. Instead of reinventing the wheel every time you sit down
to a new project, check out a copy of CakePHP and get started with the real guts of
your application.
CakePHP v1.2
7
CakePHP has an active developer team and community, bringing great value to the
project. In addition to keeping you from re-inventing, using CakePHP means your
application’s core is well tested and is being constantly improved.
• Flexible licensing
• Application scaffolding
• Code generation
• Built-in validation
• Data Sanitization
• Localization
• Works from any web site subdirectory, with little to no Apache configuration
involved
T H E H I S T O RY O F C A K E P H P
CakePHP v1.2
8
In 2005, Michal Tatarynowicz wrote a minimal version of a rapid application
framework in PHP. Michal published the framework, dubbing it Cake, and opened it
up to a community of developers, who now maintain Cake under the name CakePHP.
http://manual.cakephp.org/
http://api.cakephp.org/
If you’re stumped, give us a holler in the CakePHP IRC channel. Someone from the
development team 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.
#cakephp @ irc.freenode.net
CakePHP v1.2
9
The CakePHP Bakery is a clearing house for all things CakePHP. Check it out for
tutorials, case studies, and code examples. Once you’re acquainted with CakePHP,
log on and share your knowledge with the community and gain instant fame and
fortune.
http://bakery.cakephp.org
CakeForge is another developer resource you can use to host your CakePHP projects
to share with others. If you’re looking for (or want to share) a killer component or a
praiseworthy plugin, check out CakeForge.
http://www.cakeforge.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.
http://www.cakephp.org
Understanding Model-View-
Controller
O V E RV I E W
Well written CakePHP applications follow the MVC (Model-View-Controller)
software design pattern. Programming using MVC separates your application into
three main parts. The model represents an application’s data, the view renders a
presentation of model data, and the contro%er handles and routes requests made by
users.
CakePHP v1.2
10
Model
View Model
Model
6 5
4
3
2. The dispatcher checks the request URL (/cakes/buy), and hands the request to
the correct controller.
3. The controller performs application specific logic. For example, it may check to
see if Ricardo has logged in.
4. The controller also uses models to gain access to the application’s data. Most
often, models represent database tables, but they could also represent LDAP
entries, RSS feeds, or files on the system. In this example, the controller uses a
model to fetch Ricardo’s last purchases from the database.
5. Once the controller has worked its magic on the data, it hands it to a view. The
view takes this data and gets it ready for presentation to the user. Views in
CakePHP most often come in HTML format, but a view could as easily be a
PDF, XML document, or JSON object depending on your needs.
6. Once the view has used the data from the controller to build a fully rendered
view, the content of that view is returned to Ricardo’s browser.
CakePHP v1.2
11
Almost every request to your application will follow this basic pattern. We’ll fill in
some Cake-specific details later on, so keep this in the back of your mind as we move
on.
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 prototype. Separation also allows
developers to make changes in one part of the application without affecting others.
If you’ve never built an application this way, it takes some getting used to, but we’re
confident that once you’ve built your first CakePHP application, you won’t want to
go back.
CakePHP v1.2
12
B AS I C P RI N CI P LE S OF CAK E P HP
The Start of Becoming a Smart Cookie
CakePHP Structure
CakePHP features Controller, Model, and View classes, but it also features some
additional classes and objects that make development in MVC a little quicker and
more enjoyable. These meta-MVC classes usually aid the main MVC classes directly.
Right now we’ll stay at a higher level, so look for the details on how to use these tools
later on.
CONTROLLER AIDS
A Component is a class that aids in controller logic. If you have some logic you want to
share between controllers (or applications), a component is usually a good fit. As an
example, the core EmailComponent class makes creating and sending emails a snap.
Rather than writing a controller method in a single controller that performs this
logic, you can package 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:
• beforeRender(), executed after controller logic, but before the view is rendered
• afterFilter(), executed after all controller logic, including the view render. There
may be no difference between afterRender() and afterFilter() unless you’ve
manually made a call to render() in your controller action and have included some
logic after that call.
VIEW AIDS
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
CakePHP v1.2
13
views. One of the core helpers, AjaxHelper, makes Ajax requests within views much
easier.
Most applications have pieces of view code that repeatedly used. 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.
MODEL AIDS
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 also are 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 to many iCal events.
• beforeFind()
• afterFind()
• beforeValidate()
• beforeSave()
• afterSave()
• beforeDelete()
• afterDelete()
The names of these methods should be descriptive enough to let you know what they
do. Be sure to get the details in the models chapter.
CakePHP v1.2
14
A P P L I C AT I O N A I D S
Both controllers and models have a parent class you can use to define application-
wide changes. AppController (located at /app/app_controller.php), and AppModel
(located at /app/app_model.php) are great places to put methods you want to share
between all controllers or models.
Although they aren’t a class or file, routes 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 interpreted by your application.
• app
• cake
• docs
• index.php
• vendors
When you download CakePHP, you will see four main folders. The app folder will be
where you work your magic: it’s where your application’s files will be placed. The cake
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. The docs folder is for
the quintessential readme, changelog, and licensing information. Finally, the vendors
folder is where you’ll place third-party PHP libraries you need to use with your
CakePHP applications.
CakePHP v1.2
15
T H E A P P F O L D E R
CakePHP’s app folder is where you will do most of your application development.
Let’s look a little closer and the folders inside of app.
• config
• Holds the (few) configuration files CakePHP uses. Database connection details,
bootstrapping, core configuration files and more should be stored
here.
• controllers
• locale
• @@@
• models
• plugins
• 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.
• vendors
• Any third-party classes or libraries should be placed here. Doing so makes them
easy to access using the vendors() function.
• views
• Presentational files are placed here: elements, error pages, helpers, layouts, and
view files.
• webroot
CakePHP v1.2
16
• 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 v1.2
17
A Typical CakePHP Request
We’ve covered the basic ingredients in CakePHP, so let’s look at how each object
works 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.
Helpers
Layout
View
View
Elements
Elements 7 DataSources
8 Elements
Models
Behaviors
5
4
6
Components
2. The Dispatcher is a CakePHP core class that interprets the URL, uses Routes to
create the correct controller instance, calls the appropriate callbacks, and calls
the correct controller methods to render a requested action.
CakePHP v1.2
18
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 at least one model.
5. After the model has retrieved the data, it is returned to the controller. Model
callbacks may apply.
6. The controller may use components to further refine the data or perform other
operations (session manipulation or sending emails, for example).
7. Once the controller has used models and components to prepare the data
sufficiently, that data is handed to the view using the controller’s set() method.
Controller callbacks may be applied before the data is sent. The view logic is
performed, which may include the use of elements and/or helpers. By default, the
view is rendered inside of a layout.
CakePHP Conventions
We’re a big fan of convention over configuration. While it takes a bit of time to learn
CakePHP’s conventions, you save time in the long run: by following convention, you
get free functionality, and you free yourself from the maintenance nightmare of
tracking config files. Convention also makes for a very uniform system development,
allowing other developers to jump in an help more easily.
CakePHP v1.2
19
The name of the class a file holds may not necessarily be found in the filename,
however. The class EmailComponent is found in a file named email.php, and the
class HtmlHelper is found in a file named html.php.
MODEL CONVENTIONS
Model classnames 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.
CONTROLLER CONVENTIONS
Controller classnames are plural, CamelCased, and end in ‘Controller.’
PeopleController, BigPeopleController, and ReallyBigPeopleController are all
examples of conventional controller names.
The first function you write for a controller might be the index() function. When a
request specifies a controller but not an action, the default CakePHP behavior is to
render the index() function of that controller. For example, a request to http://
www.example.com/apples/ maps to a call on the index() function of the
ApplesController, where as http://www.example.com/apples/view maps to a call on
the view() function of the ApplesController.
You can also simulate private and protected controller functions in CakePHP by
prepending controller function names with underscores. For protected member
visibility, controller function names should be prepended with _ (a single underscore).
For private member visibility, controller function names should be prepended with __
(two underscores).
CakePHP v1.2
20
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/views/people/get_ready.ctp.
By naming the pieces of your application using CakePHP conventions, you gain
functionality without the hassle and maintenance tethers of configuration. Here’s a
final example thatties the conventions
Now that you’ve been introduced to CakePHP’s fundamentals, you might try a run
through the CakePHP Blog Tutorial, an appendix at the end of this manual.
CakePHP v1.2
21
D E V E LOP I NG WI TH CAK E P HP
Now you’re cooking.
Requirements
• HTTP Server. Apache with mod_rewrite is preferred, but by no means required.
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
• Oracle
• SQLite
• ODBC
• ADOdb
• PEAR::DBO
Installation Preparation
GETTING CAKEPHP
There are two main ways to get a fresh copy of CakePHP. First, you can download
an archive (zip/tar.gz/tar.bz2), or you can check out a code from our repository
(SVN).
To get a fresh archive, visit our web site at http://www.cakephp.org. Follow the huge
“Download Now!” link to paradise. CakePHP downloads are hosted at CakeForge,
and you can also visit the project web site at http://cakeforge.org/projects/cakephp.
CakePHP v1.2
22
If you want to live on the edge, check out our nightly downloads at http://
cakephp.org/downloads/index/nightly. CakePHP nightlies are stable, and include
fixes between releases.
To grab a fresh copy from our SVN repository, connect to https:// svn.cakephp.org/
repo/trunk/cake and choose the version you’re after.
PERMISSIONS
CakePHP uses the /app/tmp directory for a number of different operations. Model
descriptions, cached views, and session information are just a few examples.
As such, make sure the /app/tmp directory in your cake installation is writable by the
web server user.
Installation
Installing 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, and advanced.
• Development: easy to get going, URLs for the application include the CakePHP
installation directory name, and less 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
Just place your cake install inside your web server’s document root. For example,
assuming your web server’s document root is /var/www/html, a development setup
would look like this on the filesystem:
• /var/www/html
• /cake
• /app
CakePHP v1.2
23
• /cake
• /docs
• /index.php
• /vendors
PRODUCTION
In order to utilize a production setup, you will need to have the rights to change the
document root for your web server. Choosing the production setup means the whole
domain acts as a single CakePHP application.
• /path_to_cake_install/
• /app
• /webroot (this directory is set as the document root for the web server)
• /cake
• /docs
• /index.php
• /vendors
DocumentRoot /path_to_cake_install/app/webroot
A D VA N C E D I N S T A L L AT I O N
There may be some situations where you may wish to place CakePHP’s directories
on different places on the filesystem. This may be due to a shared host restriction, or
CakePHP v1.2
24
maybe you just want a few of your apps to share the same Cake libraries. This section
describes how to spread your CakePHP directories across a filesystem.
First, realize that there are three main parts to a Cake application:
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 Cake where
you’ve put it.
To configure your Cake installation, you’ll need to make some changes to /app/
webroot/index.php. 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.
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:
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:
CakePHP v1.2
25
if (!defined('ROOT'))
{
define('ROOT', DS.'home'.DS.'me');
}
if (!defined('APP_DIR'))
{
define ('APP_DIR', 'mysite');
}
if (!defined('CAKE_CORE_INCLUDE_PATH'))
{
define('CAKE_CORE_INCLUDE_PATH', DS.'usr'.DS.'lib'.DS.'cake');
}
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.
• Make sure that an .htaccess override is allowed. In your httpd.conf, you should
have a section that defines your Directory on the server. Make sure the
AllowOverride is set to All for the correct Directory.
• Make sure you are editing the system httpd.conf rather than a user- or site-specific
httpd.conf.
• Is CakePHP missing its needed .htaccess files? This sometimes happens during
copying or moving because some operating systems treat files that start with ‘.’ as
hidden. Make sure your copy of CakePHP is from the downloads section of the site
or our SVN repository, and has been unpacked correctly.
• Make sure you are loading up mod_rewrite correctly. You should see something like
LoadModule rewrite_module libexec/httpd/mod_rewrite.so and AddModule
mod_rewrite.c in your httpd.conf.
CakePHP v1.2
26
• If you are installing CakePHP into a user directory (http://example.com/
~username), you’ll need to modify the .htaccess file in the base directory of your
CakePHP installation. Just add the line “RewriteBase /~myusername/“.
FIRE IT UP
Alright, lets see CakePHP in action. Depending on which setup you used, you should
point your browser to http://example.com/ or http://example.com/cake_install/. 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.
Configuration
D ATA B A S E C O N F I G U R AT I O N
CakePHP expects database configuration details to be in a file at app/config/
database.php. An example database configuration file can be found at app/config/
database.php.default. A finished configuration should look something like this.
The $default connection array is used unless another connection is specified by the
$useDbConfig property in a model. For example, if my application 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 var $useDbConfig = ‘legacy’; in the appropriate models.
Fill out the key/value pairs in the configuration array to best suit your needs.
CakePHP v1.2
27
Key Values
driver The name of the database driver this
configuration array is for.
Examples: mysql_connect,
mysql_pconnect, sqlite_open,
ocilogon, ociplogon.
Note that 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, listed in
an appendix to this manual. The correct naming for your tables (and the addition of
some columns) can score you some free functionality and help you avoid
configuration.
C O R E C O N F I G U R AT I O N
Application configuration in CakePHP is found in /app/config/core.php. This file is a
collection of constant definitions that determine how your application behaves.
CakePHP v1.2
28
core.php constant Description
BASE_URL Un-comment this definition if you don’t
plan to use Apache’s mod_rewrite with
CakePHP. Don’t forget to remove
your .htaccess files too.
DEBUG Changes CakePHP debugging output.
CakePHP v1.2
29
core.php constant Description
WEBSERVICES Enables the use of webservices routing
when set to true.
COMPRESS_CSS Compresses output CSS (removes
comments, whitespace, repeating tags)
when set to true.
The Configure class can also 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.
R O U T E S C O N F I G U R AT I O N
Routing 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.
Routes in CakePHP 1.2 have been expanded and can be very powerful.
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
CakePHP v1.2
30
URL by putting its name in the request. You can also pass parameters to your
controller actions using the URL.
http://example.com/controller/action/param1/param2/param3
The URL /posts/view maps to the view() action of the PostsController, and /
products/viewClearance maps to the view_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.
New in CakePHP 1.2 is the ability to use named parameters. You can name
parameters and send their values using the URL. A request for /posts/view/
title:first+post/category:general would result in a call to the view() action of
the PostsController. In that action, you’d find the values of the title and category
parameters inside $this->passedArgs[‘title’] and $this->passedArgs
[‘category’] respectively.
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’;
CakePHP v1.2
31
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 the default values for custom route elements, and regular expression rules to help
the router match elements in the URL.
Router::connect(
‘URL’,
array(‘paramName’ => ‘defaultValue’),
array(‘paramName’ => ‘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 custom route elements (URL elements prefixed with a colon). Using a wildcard
tells the router what sorts of URLs you want to match, and specifying route elements
allows you to gather parameters for your controller actions.
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 the array are the default values for those keys. Let’s look at some basic
examples before we start using the third parameter of connect().
Router::connect(
‘/pages/*’,
array(‘controller’ => ‘pages’, ‘action’ => ‘display’)
);
This route is found in the routes.php file distributed with CakePHP (line 40). This
route matches any URL starting with /pages/ and hands it to the display() method
of the PagesController(); The request /pages/products would be mapped to
PagesController->display(‘products’), for example.
CakePHP v1.2
32
Router::connect(
‘/government’,
array(‘controller’ => ‘products’, ‘action’ => ‘display’, 5)
);
This second example shows how you can use the second parameter of connect() to
define default parameters. If you built a site that features products for different
categories of customers, you might consider creating a route. This allows you link
to /government rather than /products/display/5.
For additional flexibility, you can specify custom route elements. 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 custom route elements are
found in $this->params of the controller. This is different than named parameters
are handled, so note the difference: named parameters (/controller/action/
name:value) are found in $this->passedArgs, whereas custom route element data is
found in $this->params. When you define a custom route element, you also need to
specify a regular expression - this tells CakePHP how to know if the URL is correctly
formed or not.
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 that looks 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 a matching regular
expression in the third parameter of connect(). This tells CakePHP how to
recognize the ID in the URL as opposed to something else, such as an action name.
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-
>params[‘id’].
CakePHP v1.2
33
Router::connect(
'/:controller/:year/:month/:day',
array('action' => 'index', 'day' => null),
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. We set the day parameter (the fourth element in the URL) to
null to flag it as being optional.
Finally, we specify some regular expressions that will match years, months and days in
numerical form.
CUSTOM INFLECTIONS
Cake’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, editing the custom inflections configuration file is where
you can tell CakePHP about your special cases. This file is found in /app/config/
inflections.php.
In this file, you will find six variables. Each allows you to fine-tune CakePHP
inflection behavior.
CakePHP v1.2
34
inflections.php variable Description
$pluralRules This array contains regular expression
rules for pluralizing special cases. The
keys of the array are patterns, and the
values are replacements.
$uninflectedPlural An array containing words that do not
need to be modified in order to be plural
(mass nouns, etc.).
$irregularPlural An array containing words and their
plurals. The keys of the array contain the
singular form, the values, plural forms.
This array should be used to store words
that don’t follow rules defined in
$pluralRules.
$singularRules Same as with $pluralRules, only this
array holds rules that singularize words.
$uninflectedSingular Same as with $uninflectedPlural, only
this array holds words that have no
singular form. This is set equal to
$uninflectedPlural by default.
$irregularSingular Same as with $irregularPlural, only with
words in singular form.
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.
CakePHP v1.2
35
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 handy place
to use controller callbacks and define methods to be used by all of your controllers.
Controllers
INTRODUCTION
A controller is used to manage the logic for a part of your application. Most
commonly, controllers are used to manage the logic for a single model. For example,
if you were building a site for an online bakery, you might have a RecipesController
and a IngredientsController managing your reciepes and their ingredients. In
CakePHP, controllers are named after the model they handle, in plural form.
Your application’s controllers are classes that extend the CakePHP AppController
class, which in turn extends a core Controller class. The AppController class can be
defined in /app/app_controller.php and it should contain methods that are shared
between all of your application’s controllers. It extends the Controller class which is
a standard CakePHP library.
Controllers can include any number of methods which are usually referred to as
actions. Actions are controller methods used to display views. An action is a single
method of a controller. CakePHP’s dispatcher calls actions when an incoming
request matches a URL to a controller’s action. Returning to our online bakery
example, our RecipesController might contain the view(), share(), and search()
actions. The controller would be found in /app/controllers/recipes_controller.php and
contain:
CakePHP v1.2
36
<?php
function search($query)
{
//action logic goes here..
}
}
?>
/app/contro%ers/recipes_contro%er.php
In order for you to use a controller effectively in your own application, we’ll cover
some of the core attributes and methods provided by CakePHP’s controllers.
CONTROLLER AT TRIBUTES
For a complete list of controller attributes and their descriptions visit the
CakePHP API. Check out http://api.cakephp.org/1.2/classController.html.
$name
PHP4 users should start out their controller definitions using the $name attribute.
The $name attribute should be set to the name of the controller. Usually this is just
the plural form of the primary model the This takes care of some PHP4 classname
oddities and helps CakePHP resolve naming.
CakePHP v1.2
37
<?php
?>
The next most oft used controller attributes tell CakePHP what helpers,
components, and models you’ll be using in conjunction with the current controller.
Using these attributes make these MVC classes available to the controller as a class
variable ($this->ModelName, for example).
Please note that each controller has a some of these classes available by default, so
you may not need to configure your controller to use additional classes.
Let’s look at how to tell a CakePHP controller that you plan to use additional MVC
classes.
<?php
?>
CakePHP v1.2
38
When defining these attributes, make sure to include the default classes (like
including the HtmlHelper in the $helpers array, for example) if you intend to use
them.
A few attributes exist in CakePHP controllers that give you control over how your
views set inside of a layout.
The $layout attribute can be set to the name of a layout saved in /app/views/
layouts. You specify a layout by setting $layout equal to the name of the layout file
minus the .ctp extension. If this attribute has not been defined, CakePHP renders
the default layout. If you haven’t defined one at /app/views/default.ctp,
CakePHP’s core default layout will be rendered.
<?php
?>
The $pageTitle controller attribute allows you to set the title of the rendered page.
In order for this to work properly, your layout needs to include the
$title_for_layout variable, at least between <title> tags in the head of the HTML
document.
Just set $pageTitle to a string you’d like to see in the <title> of your document.
CakePHP v1.2
39
$this->data
Used to handle POST data sent from HTML Helper forms to the controller.
$html->input('User/first_name');
$this->params[’form’]
Any POST data from any form is stored here, including information also found in
$_FILES.
$this->params[’bare’]
$this->params[’isAjax’]
$this->params[’contro%er’]
Stores the name of the current controller handling the request. For example, if the
URL /posts/view/1 was requested, $this->params['controller'] would equal
“posts”.
$this->params[’action’]
Stores the name of the current action handling the request. For example, if the
URL /posts/view/1 was requested, $this->params['action'] would equal “view”.
$this->params[’pass’]
Stores the GET query string passed with the current request. For example, if the
URL /posts/view/?var1=3&var2=4 was requested, $this->params['pass'] would
equal “?var1=3&var2=4”.
CakePHP v1.2
40
$this->params[’url’]
Stores the current URL requested, along with key-value pairs of get variables. For
example, if the URL /posts/view/?var1=3&var2=4 was called, $this->params
['url'] would contain:
[var1] => 3
[var2] => 4
Other Attributes
While you can check out the details for all controller attributes in the API, there are
other controller attributes that merit their own sections in the manual.
The $cacheAction attribute aids in caching views, and the $paginate attribute is
used to set pagination defaults for the controller. For more information on how to
use these attributes, check out their respective sections in later on in this manual.
CONTROLLER METHODS
For a complete list of controller mthods and their descriptions visit the
CakePHP API. Check out http://api.cakephp.org/1.2/
classController.html#_details.
The set() method is the main way to get data from your controller to your view. Once
you’ve used set(), the variable can be accessed in your view.
CakePHP v1.2
41
//First you pass data from the controller:
$this->set(‘color’, “pink”);
<p>You have selected <?php echo $color; ?> icing for the cake.</p>
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 given in 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/views/recipes/
search.ctp will be rendered.
Although CakePHP will automatically call it after every action’s logic, you can use it
to specify an alternate view file by specifying an action name in the controller using
$action. You can also specify an alternate view file using the third parameter, $file.
When using $file, don’t forget to utilize a few of CakePHP’s global constants (such as
VIEWS).
The $layout parameter allows you to specify the layout the view is rendered in .
Flow Control
The flow control method you’ll use most often is redirect(). This method takes its
first parameter in the form of a CakePHP-relative URL. When a user has
successfully placed an order, you might wish to redirect them to a receipt screen.
CakePHP v1.2
42
function placeOrder()
{
//Logic for finalizing order goes here
if($success)
{
$this->redirect(‘/orders/thanks’);
}
else
{
$this->redirect(‘/orders/confirm’);
}
}
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.
This method does not exit() after a redirect, so set the third parameter to true if you
would rather have application execution halt after a redirect.
Similarly, 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.
Callbacks
CakePHP controllers come fitted with callbacks you can use to insert logic just
before or after controller actions are rendered.
beforeFilter()
CakePHP v1.2
43
This function is executed before every action in the controller. Its a handy place to
check for an active session or inspect user permissions.
beforeRender()
Called after controller action logic, but before the view is rendered. This callback is
not used often, but may be needed if you are calling render() manually before the end
of a given action.
afterFilter()
afterRender()
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 in your Flash AMF Remoting requests, constructClasses() may come in
handy.
referrer()
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.
Use this method to turn a set of POSTed model data (from HtmlHelper-compatible
inputs) into set of find conditions 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 v1.2
44
CakePHP’s Form- and HtmlHelpers 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:
function index()
{
$o = $this->Orders->findAll($this->postConditions($this->data));
$this->set(‘orders’, $o);
}
If you want use a different SQL operator between terms, supply them using the
second parameter.
/*
Contents of $this->data
array(
‘Order’ => array(
‘num_items’ => ‘4’,
‘referrer’ => ‘Ye Olde’
)
)
*/
//Let’s get orders that have at least 4 items and contain ‘Ye Olde’
$o = $this->Order->findAll($this->postConditions(
$this->data,
array(‘>=’, ‘LIKE’)
));
The key in specifying the operators is the order of the columns in the $this->data
array. Since num_items is first, the >= operator applies to it.
The third parameter allows you to tell CakePHP what SQL boolean operator to use
between the find conditions. String 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.
CakePHP v1.2
45
This convenience method concatenates date parts in $this->data back together
before storage. If you have Form- or HtmlHelper date inputs, this method
concatentates the year, month, day and time into a more storage-compatible string.
This method uses the controller’s default model (i.e., the Cookie model for the
CookiesController) as the target for the concatenation, but an alternate class can be
supplied using the first parameter.
paginate()
This method is used for paginating results fetched by your models. You can specify
page sizes, model find conditions and more. Details on this method follow later.
Check out the pagination chapter later on in this manual.
This function calls a controller’s action from any location and returns data from the
action. The $url passed is a CakePHP-relative URL (/controllername/actionname/
params). If the $options array includes a ‘return’ key, AutoRender is automatically set
to true for the controller action, having requestAction hand you back a fully
rendered view.
Note: while you can use requestAction() to retrieve a fully rendered view, the
performance hit you take on running through the whole view layer another time isn’t
often worth it. The requestAction() method is best used in conjunction with
elements–as a way to fetch business logic for an element before rendering.
First, let’s look at how to get data from a controller action. First, we need to set up a
controller action that returns some data we might need in various places throughout
the applicaiton:
CakePHP v1.2
46
Imagine that we needed to create a simple table showing the active users in the
system. Instead of duplicating list-generating code in another controller, we can get
the data from UsersController->getUserList() instead by using requestAction().
// Now the $users variable in the view will have the data from
// UsersController::getUserList().
}
}
If you have an element in your application that is not static, you might want to use
requestAction() to grab controller-like logic for the element as you inject it into your
views. If you have created a controller action that supplies the logic needed, you can
grab that data and pass it to the second parameter of the view’s renderElement()
method using requestAction().
<?php
echo $this->renderElement(
‘users’,
$this->requestAction(‘/users/getUserList’)
);
?>
If the $options array contains a ‘return’ key, the controller action is rendered inside
an empty layout and returned. In this way, the requestAction() function is also useful
in Ajax situations where a small element of a view needs to be populated before or
during an Ajax update.
CakePHP v1.2
47
Components
INTRODUCTION
Components are packages of logic that are shared between controllers. If you find
yourself wanting to copy and paste things between controllers, you might consider
wrapping some functionality in a component.
CakePHP also comes with a fantastic set of core components you can use to aid in:
• Security
• Sessions
• Emails
• Cookies
• Authentication
• Request handling
Each of these core components are detailed in their own chapters. For now, we’ll
show you how to create your own components. Creating components keeps
controller code clean and allows you to reuse code between projects.
C R E AT I N G Y O U R O W N
Suppose our online application needs to interface with a legacy system that tracks
inventory in our brick-and-mortar stores. Let’s create an example component that
connects to the inventory management system and checks the inventory for a given
product (specified by a product ID).
The first step is to create a new component file and class. Create the file in /app/
controllers/components/inventory.php. The basic structure for the component
would look something like this.
CakePHP v1.2
48
<?php
return $in_stock;
}
}
?>
To get access to the controller instance from within your newly created component,
you’ll need to implement the startup() method. This special method takes a reference
to the controller as its first parameter, and is automatically called after beforeFilter()
in the controller. This allows you to set component properties in the beforeFilter
method, which the component can act on in it’s startup() method.
To get access to models inside a component, just create new instances manually. For
this component, we might need access to a Product model to do lookups.
CakePHP v1.2
49
<?php
function startup(&$controller)
{
//Set up a model instance for this component
$this->Product &= new Product();
}
function getInStock($pid)
{
//Do inventory logic here...
}
}
?>
You might also want to utilize other components inside a custom component. To do
so, just create a $components class variable (just like you would in a controller) as an
array that holds the names of components you wish to utilize.
Models
INTRODUCTION
Models are used in CakePHP applications for data access. Models most commonly
represent a database table, though they can be used to access files, LDAP records,
iCal events, or rows in a CSV file.
Models can also be associated in order to make data access easier. If you find that
every time you fetch data with your Recipe model, you’d also like to fetch its related
Author and Ingredient entries, model associations can help.
CakePHP v1.2
50
<?php
?>
With just a few lines of code, you have full CRUD (create, retrieve, update, delete)
functionality. Most of this comes from the model’s grandparent class Model. Once
you have a model defined, you can utilize it in your controller. Here’s a basic example
of a model being used ($this->Ingredient) to fetch a list of Ingredient records to
hand to the view:
<?php
function index()
{
//Hand a list of all ingredients to the view:
$this->set(
‘ingredients’,
$this->Ingredient->findAll();
)
}
}
?>
Using models to fetch and save data can save you serious headaches. CakePHP
models provide a standard and centralized way to approach your data store, while at
the same time offering a level of security for your application. @@@
MODEL ATTRIBUTES
For a complete list of model attributes and their descriptions visit the
CakePHP API. Check out http://api.cakephp.org/1.2/classModel.html.
CakePHP v1.2
51
$useDbConfig
The name of the database configuration this model uses. Configurations are specified
in /app/config/database.php as class variables. Defaults to ‘default.’
$useTable
The name of the database table this model uses. The default table used is the lower-
cased, plural version of the model’s classname. Set this attribute to the name of an
alternate table, or set it to false if you wish the model to have no database relation at
all.
$primaryKey
If the model is tied to a database table, this attribute holds the name of the column
of the tables primary key. Defaults to ‘id’.
$displayField
This attribute is used to define the field the model will use for display in scaffolding
elements. When associated data is presented (i.e., which Ingredient you’d like to
associate to a Recipe) scaffolding will use the row’s ID, ‘name’, or ‘title’ fields unless
$displayField has been specified.
$recursive
An integer specifying the number of levels you wish CakePHP to fetch associated
model data in find() and findAll() operations.
Imagine your application features Groups which 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:
• 2 - Cake fetches a Group, 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
unnecessarily slows your app.
$order
CakePHP v1.2
52
The default ordering of data for any find operation. Possible values include:
$order = “field”
$order = “Model.field”;
$id
Value of the primary key ID of the record that this model is currently pointing to. If
you are saving model data inside of a loop, set $id to null ($this->ModelName->id =
null) in order to ready the model class for the next set of data.
$data
The container for the model’s fetched data. While data returned from a model class
is normally used as returned from a find() call, you may need to access information
stored in $data inside of model callbacks.
$_tableInfo
Contains metadata describing the model’s database table fields. Each field is
described by:
• name
• null
• default value
• length
$validate
CakePHP v1.2
53
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.
For more information on validation, see the Data Validation chapter later on in this
manual.
$name
The name of this model. CakePHP model names are CamelCased and singular.
CakePHP users on PHP4 should always declare $name in order to avoid the quirks
involved with PHP4 class-naming.
$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.
These model attributes will be covered later in this chapter. They hold arrays that
define how this model is related to others.
$actsAs
This attribute is also covered later on. The $actsAs attribute stores the names of
Behaviors this model uses.
MODEL METHODS
For a complete list of model methods and their descriptions visit the
CakePHP API. Check out http://api.cakephp.org/1.2/classModel.html.
findAll(string $conditions, array $fields, string $order, int $limit, int $page, int
$recursive);
Returns the specified fields up to $limit records matching $conditions (if any), start
listing from page $page (default is page 1). The $conditions should be formed just as
CakePHP v1.2
54
they would in an SQL statement: $conditions = "Pastry.type LIKE '%cake%'
AND Pastry.created_on > ‘2007-01-01’", for example. Prefixing conditions with
the model’s name (‘Pastry.type’ rather that just ‘type’) is always a good practice,
especially when associated data is being fetched in a query.
CakePHP v1.2
55
Array
(
[0] => Array
(
[ModelName] => Array
(
[id] => 83
[field1] => value1
[field2] => value2
[field3] => value3
)
Same as in findAll(), except that find only returns the first record matching the
supplied $conditions.
findAllBy<fieldName>(string $value)
CakePHP v1.2
56
findBy<fieldName>(string $value);
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.
The returned result is an array formatted just as would be from find() or findAll().
Returns an array containing the neighboring models (with only the specified fields),
specified by $field and $value, filtered by the SQL conditions, $conditions.
This is helpful shortcut in creating ‘Previous’ and ‘Next’ links that walk users through
some ordered sequence through your model entries. It only works for numeric and
date based fields.
CakePHP v1.2
57
This gives us the full $image[’Image’] array, along with $neighbors[’prev’][’Image’]
[’id’] and $neighbours[’next’][’Image’][’id’] for use in the view.
Returns the value of a single field, specified as $name, from the first record matched
by $conditions as ordered by $order.
findCount(string $conditions)
This function is a shortcut to getting a list of key value pairs - especially handy for
creating an HTML select tag from a list of your models. Use the $conditions,
$order, and $limit parameters just as you would for a findAll() request.
The $keyPath and $valuePath specify where to find the keys and values for your
generated list. For example, if you wanted to generate a list of roles based on your
Role model, keyed by their integer ids, the full call might look something like:
$this->Role->generateList(
null,
'role_name ASC',
null,
'{n}.Role.id',
'{n}.Role.role_name'
);
Many people are a little bewildered by the ‘{n}’ syntax used by generateList(). Fret
not, for it serves as a place holder for switching between model DataSources, covered
later on in this chapter.
CakePHP v1.2
58
Custom SQL calls can be made using the model’s query() and execute() methods.
The difference between the two is that query() is used to make custom SQL queries
(the results of which are returned), and execute() is used to make custom SQL
commands (which supply no return value).
If you’re ever using custom SQL queries in your application, be sure to check out
CakePHP’s Sanitize library (covered later in this manual), which aids in cleaning up
user-provided data from injection and cross-site scripting attacks.
Most of the model’s find calls involve passing sets of conditions in one way or
another. The simplest approach to this is to use a WHERE clause snippet of SQL. If
you find yourself needing more control, you can use arrays.
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 discreet, manipulatable parts. This allows CakePHP to generate the most
efficient query possible, ensure proper SQL syntax, and properly escape each
individual part of the query.
The structure here is fairly self-explanatory: it will find any post where the title
equals “This is a post”. Note that we could have used just “title” as the field name,
but when building queries, it is good practice to 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”:
CakePHP v1.2
59
Notice the ‘<>’ that prefixes the expression. 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 the operator and the value. The one
exception here is IN (...)-style matches. Let’s say you wanted to find posts where the
title was in a given set of values:
array(
"Post.title" => array("First post", "Second post", "Third post")
)
array
(
"Post.title" => array("First post", "Second post", "Third post"),
"Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
)
By default, CakePHP joins multiple conditions with boolean AND; which means,
the snippet above 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"))
)
)
Cake 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 nest-able. Let’s say you had a hasMany/belongsTo relationship between
Posts and Authors, which would result in a LEFT JOIN. 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 want to restrict your search to posts written by Bob:
CakePHP v1.2
60
array
("Author.name" => "Bob", "or" => array
(
"Post.title" => "LIKE %magic%",
"Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks")
)
)
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
HtmlHelper, FormHelper, and find methods all package data in this format. If you’re
using either of the helpers, the data is also conveniently available in $this->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:
CakePHP v1.2
61
function edit($id)
{
//Has any form data been POSTed?
if(!empty($this->data))
{
//If the form data can be validated and saved...
if($this->Recipe->save($this->data[‘Recipe’]))
{
//Set a session flash message and redirect.
$this->Session->setFlash(“Recipe Saved!”);
$this->redirect(‘/recipes’);
exit(0);
}
}
One additional note: when save is called, the data passed to it in the first parameter
is validated using CakePHP validation mechanism (see the Data Validation chapter
for more information). If for some reason your data isn’t saving, be sure to check to
see if some validation rules aren’t being broken.
There are a few other save-related methods in the model that you’ll find useful:
Featured above, this method saves array-formatted data. The second parameter
allows you to sidestep validation, and the third allows you to supply a list of model
fields to be saved. For added security, you can limit the saved fields to those listed in
$fieldList.
This method initializes the model class for saving new information. If you’re ever
calling save() inside of a loop (in order to create many records at once), make sure to
call create() just before save().
If you supply the $data parameter (using the array format outlined above), the newly
created model will be ready to save with that data (accessible at $this->data).
CakePHP v1.2
62
Used to save a single field value. Set the ID of the model ($this->ModelName->id =
$id) just before calling saveField().
Updates many records in a single call. Records to be updated are identified by the
$conditions array, and fields to be updated, along with their values, are identified by
the $fields array.
For example, if I wanted to approve all bakers who have been members for over a
year, the update call might look something like:
$this->Baker->updateAll(
array(‘Baker.created’ => “<= $this_year”),
array(‘Baker.approved’ => 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:
• if $cascade is set to true, the related Recipe records are also deleted.
• if $cascade is set to false, the Recipe records will remain after the User has been
deleted.
Same as with del() and remove(), except that deleteAll() deletes all records that
match the supplied conditions. The $conditions array should be supplied as an SQL
fragment or array.
Model Callbacks
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
CakePHP v1.2
63
AppModel) class. Be sure to note the expected return values for each of these special
functions.
beforeFind(mixed $queryData)
Called before any find-related operation. The $queryData 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 $queryData options), return false.
You might use this callback to restrict find operations based on a user’s role, or make
caching decisions based on the current load.
afterFind(array $results)
Use this callback to modify results that have been returned from a find operation, or
to perform any other post-find logic. The $results parameter passed to this callback
contains the returned results from the model’s find operation
The return value for this callback should be the (possibly modified) results for the
find operation that triggered this callback.
beforeValidate()
Use this callback to modify model data before it is validated. It can also be used to
add additional, more complex validation rules using Model::invalidate(). In this
context, model data is accessible via $this->data. This function must also return
true, otherwise the current save() execution will abort.
beforeSave()
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 to continue.
This callback is especially handy for any data-massaging logic that needs to happen
before your data is stored. If your storage engine needs dates in a specific format,
access it at $this->data and modify it.
afterSave()
CakePHP v1.2
64
If you have logic you need to be executed just after every save operation, place it in
this callback method.
beforeDelete()
Place any pre-deletion logic in this function. This function should return true if you
want the deletion to continue, and false if you want to abort.
afterDelete()
Place any logic that you want to be executed after every deletion in this callback
method.
User-Defined Functions
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.
Any operation that handles the saving and fetching of data is best housed in your
model classes.
A S S O C I AT I O N S
Introduction
One of the most powerful features of CakePHP is the relational mapping provided
by the model. In CakePHP, the links between models are handled through
associations.
The purpose of this section is to show you how to plan for, define, and utilize
associations between models in CakePHP.
CakePHP v1.2
65
The four association types in CakePHP are: hasOne, hasMany, belongsTo, and
hasAndBelongsToMany (HABTM).
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
complete as a multidimensional array used to define association specifics.
<?php
?>
hasOne
First, your database tables need to be keyed correctly. For a hasOne relationship to
work, one table has to contain a foreign 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:
CakePHP v1.2
66
The User model file will be saved in /app/models/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/models/profile.php, or the association won’t work.
<?php
?>
There are two ways to describe this relationship in your model files. The simplest
method is to set the $hasOne attribute to a string containing the classname 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 sort related rows in descending order according to a
date, or you might want to limit the association to include only certain records.
<?php
?>
• className: the classname of the model being associated to the current model. If
you’re defining a ‘User hasOne Profile’ relationship, the className key should
equal ‘Profile.’
CakePHP v1.2
67
• 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 other model, suffixed
with ‘_id’.
• conditions: An SQL fragment used filter related model records. It’s good practice
to use model names in SQL fragments: “Profile.approved = 1” is always better than
just “approved = 1.”
• fields: A list of fields to be retrieved when the associated model data is fetched.
Returns all fields by default.
• dependent: When the dependent key is set to true, and the model’s delete()
method is called with the cascade parameter 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 it exists:
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
)
)
belongsTo
CakePHP v1.2
68
Now that we have Profile data access from the User model, let’s define a belongsTo
association in the Profile model in order 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:
<?php
?>
<?php
?>
CakePHP v1.2
69
Possible keys for belongsTo association arrays include:
• className: the classname 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 other model. This is
especially handy if you need to define multiple belongsTo relationships. The default
value for this key is the underscored, singular name of the other model, suffixed
with ‘_id’.
• conditions: An SQL fragment used filter related model records. It’s good practice
to use model names in SQL fragments: “User.active = 1” is always better than just
“active = 1.”
• fields: A list of fields to be retrieved when the associated model data is fetched.
Returns all fields by default.
Once this association has been defined, find operations on the Profile model will also
fetch a related User record if it exists:
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
)
)
hasMany
CakePHP v1.2
70
When keying your database tables for a hasMany relationship, follow this
convention:
<?php
?>
<?php
?>
CakePHP v1.2
71
• className: the classname 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 other model, suffixed
with ‘_id’.
• conditions: An SQL fragment used filter related model records. It’s good practice
to use model names in SQL fragments: “Comment.status = 1” is always better than
just “status = 1.”
• fields: A list of fields to be retrieved when the associated model data is fetched.
Returns all fields by default.
• order: An SQL fragment that defines the sorting order for the returned associated
rows.
• offset: The number of associated rows to skip over (given the current conditions and
order) before fetching and associating.
• Note: the second parameter of the Model->delete() method must be set to true in
order for recursive deletion to occur.
• finderQuery: A complete SQL query CakePHP can use to fetch associated model
records. This should be used in situations that require very custom results.
Once this association has been defined, find operations on the User model will also
fetch related Comment records if they exist:
CakePHP v1.2
72
//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] => 123
[user_id] => 121
[title] => More on Gwoo
[body] => But what of the ‘Nut?
[created] => 2006-05-01 10:41:01
)
)
)
hasAndBelongsToMany (HABTM)
Alright. At this point, you can already call yourself a CakePHP model associations
professional. You’re already well versed in the three associations that take up the bulk
of object relations.
CakePHP v1.2
73
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
a Tag model using HABTM. Attaching the “Italian” tag to my grandma’s Gnocci
recipe doesn’t “use up” the tag. I can also tag my Honey Glazed BBQ Spaghettio’s
with “Italian” if I want to.
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. The contents of the table should be at least two
fields, each foreign keys (which should be integers) pointing to both of the involved
models.
HABTM: Requires a separate join table that includes both model names.
Once this new table has been created, we can define the HABTM association in the
model files. We’re gonna skip straight to the array syntax this time:
CakePHP v1.2
74
<?php
?>
• className: the classname of the model being associated to the current model. If
you’re defining a ‘User hasMany Comment’ relationship, the className key should
equal ‘Comment.’
• 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).
• foreignKey: 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 name of the other model, suffixed
with ‘_id’.
• associationForeignKey: the name of the foreign key found in the current model.
This is especially handy if you need to define multiple HABTM relationships. The
default value for this key is the underscored, singular name of the current model,
suffixed with ‘_id’.
• conditions: An SQL fragment used filter related model records. It’s good practice
to use model names in SQL fragments: “Comment.status = 1” is always better than
just “status = 1.”
CakePHP v1.2
75
• fields: A list of fields to be retrieved when the associated model data is fetched.
Returns all fields by default.
• order: An SQL fragment that defines the sorting order for the returned associated
rows.
• offset: The number of associated rows to skip over (given the current conditions and
order) before fetching and associating.
• finderQuery: A complete SQL query CakePHP can use to fetch associated model
records. This should be used in situations that require very custom results.
Once this association has been defined, find operations on the Recipe model will also
fetch related Tag records if they exist:
Array
(
[Recipe] => Array
(
[id] => 2745
[name] => Chocolate Frosted Sugar Bombs
[created] => 2007-05-01 10:31:01
[user_id] => 2346
)
[Tag] => Array
(
[0] => Array
(
[id] => 123
[name] => Breakfast
)
[1] => Array
(
[id] => 123
[name] => Dessert
)
[2] => Array
(
[id] => 123
[name] => Heart Disease
)
)
)
CakePHP v1.2
76
Remember to define a HABTM association in the Tag model if you’d like to fetch
Recipe data when using the Tag model.
When working with associated models, it is important to realize that saving model
data should always be done by the corresponding 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 their related 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 saving of 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.
function add()
{
if (!empty($this->data))
{
//We can save the User data:
//it should be in $this->data['User']
$this->User->save($this->data);
$user_id = $this->User->getLastInsertId();
$this->data['Profile']['user_id'] = $user_id;
$this->User->Profile->save($this->data);
}
}
CakePHP v1.2
77
As a rule, when working with hasOne, hasMany, and belongsTo associations, its 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 getLastInsertId()
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.
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, and everything gets
linked up correctly.
With HABTM, it’s a bit trickier, but we’ve gone out of our way to make it as simple
as possible. In keeping along with our example, we’ll need to make some sort of form
that relates Tag to Recipe. Let’s now create a form that creates recipes, and associates
them to an existing list of tags.
You might actually like to create a form that creates new tags and associates them on
the fly - but for simplicity’s sake, we’ll just show you how to associate them and let
you take it from there.
Let’s just start out with the part of the form that creates a recipe:
//@@@
asdf
D ATA S O U RC E S
asdf
B E H AV I O R S
asdf
CakePHP v1.2
78