“Hartjes’ book takes an in-depth look at pitfalls common to many legacy
web applications, particularly those written in PHP.”
ROL) Det BCR Us MND)
Oa Elda
Refactoring Lega
Aaa ns
CakePHRefactoring Legacy Applications using CakePHP
by Chris HartjesPreface
My thanks go out to various members of the CakePHP community for providing me with help and advice while
Iwas writing this book. I apologize to those who I omit here but there are three I wish to single out
+ Nate Abele for always making fun of me, but always answering my questions too
* Garrett Woodworth for patiently explaining the inner workings of CakePHP to me
* Joel Parras for his technical editing work:
I also wish to thank my wife Claire for putting up with the seemingly-endless hours I spend on my computer
working on various projects. Itis only because of her support of my efforts that I am able to do things like this.
‘What this book is NOT is an introduction to programming with CakePHP. There are already books
that do a good job of giving you the basics of using CakePHP. If this book is going to help you at all,
you really need to have the following qualities:
* Understand PHP beyond
* Understand CakePHP beyond just using CakePHP’s built-in scaffolding
* Actually understand the application you're trying to refactor
Without those three qualities, I can almost predict that your refactor will be a failure
This book was produced with the help of the following tools
vim (http://www. vim. org)
reStructured Text (http /docutils sourceforge netrst html)
rst2pdf (http //eode. google com/p/rst2pdf)
PHP (http://www php.net)
Pages (http://www. apple. com/iw ork/pages/)
CakePHP (http //nvww.cakephp org)
All code examples were meant to be run under PHP 5, with development happening using PHP 5.2.8
All CakePHP examples were written and tested using the “bleeding-edge” version of CakePHP 1.2
from the SVN repository at https’//svn. cakephp.org/repo/branches/1.2.x.x. It was at revision 8052
at the time of publishing While every effort has been made to ensure that the code samples work as
advertised and are bug free, I offer no warranty in regards to their usage in your own code. Use the
samples at your own risk
CakePHP is a registered trademark of the Cake Software Foundation (http://cakefoundation. org)
To contact the author, visit the web site for this book at http://www littlehart net/book or visit theauthor’s blog at http /wrwv littlehart net/atthekeyboard. Feedback and questions are always welcome
and while I read every email and comments that comes in, time constraints might prevent me from
responding
All content, unless otherwise noted, is (c)2009 Chris Hartjes. In lieu of adding Digital Rights Man-
agement to this document I ask that people respect copyright laws and not distribute copies of this
book without my express written permission.Table of Contents
Introduction - Frameworks Get No Respect
Legacy Applications
Chapter 1 - Understanding Models
Turning Tables Into Models
Model Associations
No Auto-incrementing Primary Key
Updating Denormalized Data
Removing Cached Data Upon Record Deletion
Making Your Models Behave: Behaviors
Request Routing
Chapter 2 - Creating Controllers
Determining Controller Names
Linking models to controllers
Components for your controllers
Chapter 3 - Layouts
Setting Character Sets
Setting Page Titles
Javascript Usage
Actual Content
Css
META content
Chapter 4 - Separating Business and Presentation Logic
Helpers for your views
Chapter 5 -- Untwhirling The Spaghetti
Fat Models, Skinny Controllers
Hopelessly Twisted
12
12
15
15
16
16
17
18
18
18
19
20
20
22
22
22Easy Drop-Down Lists
Model Association Tweaking
Multiple Select Drop-Downs
Chapter 6 -- Refactoring Multiple Record Editing
Simplified Views
Filtering and Updating Records
Only Updating The Data You Need To
Chapter 7 -- Easy Batch Record Manipulation
Using N.field Notation In Your Forms
Chapter 8 -- Easy Batch Record Addition
Easy Arrays In Forms
Saving and Validating Data
Chapter 9 -- Displaying Data
Simple Data Collection For Display
Grouping Display Output By Criteria
Business Logic Where It Belongs
Chapter 10 -- Playing With Date Ranges
Real World Date Forms
Date Form Automagic
Chapter 11 —- Wrapping It All Up
28
29
30
37
43
46
48
65
70
72
77
79
82Refactoring Legacy Applications Using CakePHP 1
Introduction - Frameworks Get No Respect
Web frameworks just don't get much love in the PHP world these days. It seems that in any given
week I can find the following information on the intemet:
1, An announcement about the launch of a new PHP web application framework
2. Aset of benchmarks using the ever-popular "Hello World" method showing that Framework X is awe-
some and every other framework out there sucks
While it's admirable that so much effort is being put into the creation, maintenance and use of frame-
works, I think a lot of people are missing the point of what a framework is supposed to do
In my opinion, the job of a framework should be to give your application a structure that is enforced
via a set of rules established by the framework itself, and at the same time allow you to rapidly build
something.
I think most frameworks do an awesome job in regards to the first part, but often fail to provide the
tools necessary to make the second part happen.
In this book, I'm going to focus on the ability of a framework to provide your legacy application with
the structure it needs to make it more viable going forward.
Legacy Applications
Any developer who has ever gone back to an application that they wrote in the past has worked with
legacy code Although it’s used by people as a derogatory label applied to code that you don’t like,
legacy code is simply code that you wrote yesterday that you need to fix today When put into that
context, it’s not so scary any more Any developer worth his skills is asked on a daily basis to go back
into some “old and crufty code” and fix it, or even rewrite it to meet a variety of new goals or situa-
tions
Sometimes it is because a new version of the language has come out and is ready for production use,
often allowing the developer to take advantage of new features or just increased speed. But I would
say it is rare that old code is rewritten or refactored for this reason.
Sometimes it’s also because the developer is scratching an itch and wants to rewrite something using
anew language or framework because they've gotten caught up in the buzz and hype surrounding that
language. Every developer is guilty of that at one point or another, but that is usually the worst reason
to refactor something.
Finally, a refactor can also occur when a developer discovers new techniques or tools that will allow
their application to perform better. These include things like rewriting a PHP library as an extension
in C, or rewriting a Ruby daemon as a Java application (this has happened where I've worked, go
figure)
Refactoring usually happens because the code in question has started to become a maintenance
nightmare. Code so brittle that you fear making changes to libraries because a change in one place
could impact 100 different places. Code so tangled up that it’s impossible to create automated tests to2 Refactoring Legacy Applications Using CakePHP
verify that things are actually working In short, the type of code that has grown organically without
any structure. Hey, sometimes this sort of thing Just Happens. But we have a tool that help us give our
legacy application the structure it needs going forward. That tool is a web application framework
I’ve grown to appreciate just how flexible CakePHP really is once you start looking below the sur-
face. It comes with all sorts of tools that are indispensable for when you are refactoring an applica-
tion that has gone the “spaghetti PHP” way of mixing up business logic, request handling and display
logic together Because that is what this is really about: giving your legacy application a structure
that will allow it to grow while being easier to maintain.
This book is not just someone talking about how awesome CakePHP is There are enough books and
blogs out there that do that already. Instead, you will be following along as I take what can truly be
called a legacy app and rewriting it to use CakePHB, showing you much thet the framework has to
offer.
I’ve been playing in simulation baseball leagues for nearly 20 years, and I’ve written web applications
to help run those leagues. One of the tools I’ve created is called WebReg, short for Web Registrar. It’s
used to keep track of rosters and trades in the league. It’s the one piece of code I have that has been
online the longest, since 2005 at it’s current location and at least a year before that on the original
server It's used every week throughout the season
Itis, however, a mess. It’s pure spaghetti PHP - raw SQL calls, if-then-else request responses and
HTML all mixed up together in one. An application that is ripe for a rewrite with CakePHP so I can
start to implement some of the features that the current registrar has requested.
I’m hoping that by sharing my experiences with a real-world example of PHP application that needed
the refactoring and structure that a framework gives you, you will begin to truly understand and (more
importantly) appreciate what CakePHP can do for you.
Also, this book is not an exhaustive example of how to use CakePHP in the “real world”. It is an
example of porting over a fairly simple application, but the principles I use in porting this over are
applicable in almost any other situation.Refactoring Legacy Applications Using CakePHP = 3
Chapter 1 - Understanding Models
tion with a very simple web-based front end, allowing
ns in the simulation baseball league. It's natural that I would
application by looking at how we will represent the tables
in the database using CakePHP models. This applica PostgreSQL as the back-end, so some
gs might be a little different if you are used to deali for us, we don’
to worry about that because CakePHP abstracts all thos
jebReg is auch a databas
our registrar to manipulate roster
start the process of rewriting our |
Since this application shares a database and tables with another application written in CakePHP 1.2,
we are lucky enough to already have our table following the convention that
CakePHP uses
+ auto-incremented primary keys, in this case of the ‘integer’ data type
+ table names that are plural, as the CakePHP convention is to give the Model object representing
the table the singular name
jebReg has three tables that we need to womry about
SM eed
Table “public. franchises”
(en ee Type etic
Bsr ae
eee ae |
character(58) |
fer ee
ees nee]
Penta i
Our ‘franchises! table, which keeps track of teams in the league. This table does not have an auto-
cremented integer for the pr key, mainly because the number of franchises in the league will
not be exceeding 24 any time soon.
ee cee
cece tae
fot ard Dera Cay
er ea eee tne et ee
character(3) |
Sraee tl
Rts Hl
i
+
f
eee ae el
i
i
i
| integer H4 Refactoring Legacy Applications Using CakePHP
Next, we have a table that gives us
ne of the first "code smells" for this application: a table called
‘teams’ that contained information about players. By “code smells” Imean something that when y:
look at it in the context of the application, it doesn’t seem right Sometimes these types of things
easily fixable, Sometimes they are not. In this case, CakePHP itself gives us a graceful way to get
around it
This table is supposed to be tracking players, not teams. I’m trying to remember why I called it
‘teams’ in the first place. Since I’m drawing a blank, I’ll have to assume that it was for a good r
atthe time, This table should’ve been called ‘players’, but we can’t go back and fix it now.
ason,
In this table we are tracking the name of the player in the dice-and-cards game that the league uses,
along with what franchise the player belongs to in the league. The ‘comments’ field contains info
about when the player was drafted or traded for, basically a mini-transaction summary. We determine
whether a player is active or inactive through the ‘status’ field, and we differentiate between whether
someone is a batter, pitcher or draft pick (teams can trade their picks) using the ‘item _type' field
ern rer rey
Le ate
1 Type Modifiers
re) eu cg
reo} I character(3)
med ipod
coersc ie ee aes
Re te em Rt
i
i
i
Finally we have our transaction_log' table, where keep track of all transactions on a per-team basis.
Notice the primary key is called ‘trans _id’, not ‘id’ as you would expect from the CakePHP coding
standards
Turning Tables Into Model:
Tenet tp is a us to define CalePEIP models that lll sto get data fom these tables The
first thing that we should tackle is getting nid of that "code smell* I mentioned before. Instead of using
Team as the model name, let's instead call it what it should be called, Player
class Player extends Appiiodel {
public $useTable = ‘teams*;
public $belongsTo = array(
“Franchise” => array(
“classtiame’ => ‘Franchise’,
“foreignkey”
d
“ibl_team’
dsRefactoring Legacy Applications Using CakePHP 5
What is actually going on in this Model? It's actually quite simple.
public $useTable
version of the table name, we have to define what table we are actually using.
"teams";
public $belongsTo = array(
"Franchise" => array(
“classilame’ => ‘Franchise’,
“foreignKey*
)
“ibl_team’
Model Associations
Next, we are defining a relationship between two models. CakePHP supports 4 types of
relationships:
Relationship Association Type
one to one hasOne
one to many hasMany
many to one belongsTo
many to many hasAndBelongsToMany
The use of these associations allows you to use CakePHP's associated data mapping, {where abequest
ko find la one model wll im all aseied dla So, sn hs case we ae establishing tat
Player belongs to a Franchise. The other information in the association definition is important as well.
I tend to only define the parameters that I need, but you can feel free to set other parameters to their
defaults if so desired. Later in this chapter I talk more about those parameters, but for now we only
need these ones
It's important to note here that you do NOT have to have integer-only foreign keys. As long as it's a
valid column in your table, CakePHP can use it in the queries it generates just fine6 Refactoring Legacy Applications Using CakePHP
class Franchise extends Apptodel {
public $primarykey = ‘nickname’ ;
public $hasMany = array(
“Player’ => array(
“classilame’ => ‘Player’,
“foreignKey’ => ‘ibl_team’
»»
“TransactionLog’ => array(‘classtame"
“TransactionLog",
“ibl_team*
“foreignkey”
The code in the Franchise model is very similar, except you can see how we've added in another asso-
ciation. A franchise has many entries in the transaction log, so it makes sense to define the association
between them
class TransactionLog extends Appiodel {
public $useTable = ‘transaction_log" ;
public $primarykey = ‘trans_id’;
public $belongsTo = array(
"Franchise" => array(
“Franchise’,
*classtlame*
“foreignKey’ => ‘ibl_team’,
Previously I mentioned " code smells*, the little signs that perhaps you've made an architectural mis-
take in your code or your database schema. In this case we have a primary key that does not follow
the CakePHP naming convention of just being ‘id. That's easy enough to fixRefactoring Legacy Applications Using CakePHP = 7
This simply tells CakePHP to use the column 'trans_id' as the primary key for any queries
public $belongsTo
rray(
"Franchise" => array(
*classtlame*
"Franchise",
“foreignKey’ => ‘ibl_team’
)
3
Much like our hasMany association, this just creates the reverse association for these tables, where
transaction log entries belong to a specific team.
Now that we have our three models defined, let's explore some common "what if" scenarios you
might encounter when porting your legacy application over
No Auto-incrementing Primary Key
It's entirely conceivable that you could have a database structure where you have a primary key that
has not been defined, This isn't always possible, but we have a way in CakePHP to handle this issue
CakePHP has several callback methods for it's models that allow you to add in some logic just before
au aro nec fre ei Tnthis eae, the solution for code to add
// Sample beforeSave() logic
class Foo extends Appitodel() {
// primary key is a timestamp when saving a new record
public beforeSave() {
if (empty($this-rdata["id'])) {
$this-rdata[‘id’] = time();
}
return true;
}
// Example of how to call it, assuming you have data in $data
$this->Foo->set ($data) ;
if ($this->Foo->save()) {8 Refactoring Legacy Applications Using CakePHP
$this->Session->setFlash( ‘Created new record");
}
Let's take a look at what's going on here
public beforeSave() {
if (empty(Sthis-rdata["id'])) {
$this->data["id'] = time();
}
return true;
, SO it
1s the ideal place to put any logic that you need executed before you save the record. So, the logic in
there says "if we don't already have a value in our primary key column in our data set, assign it the
desired value"
Then, when the model goes to save() the record, it now has a value for the primary key and CakePHP
can do it's internal magic where it figures out if it's updating an existing record or creating a new one
Also, be sure to have your beforeSaveQ) method retum true or else your save attempt will fail
Updating Denormalized Data
At one time I worked for a company that was working on an adult dating web site, which is really
nothing more than a search engine with a very nice front end (ugh, what a bad pun). At one point,
to try and make various searches work more efficiently, it was decided to denormalize our data for
searching purposes
‘We ended up with multiple tables that contained data from our main user database, but not always the
same data and it wasn't updated with the same frequency
How would you implement something like this in CakePHP? One method would be to create an after-
Save() method in the User model that then updates all the search tables
// Assume that the data we just saved are in $this->data
class User extends Appiiodel {
public afterSave($created) {
y
* only pass through the id, username, gender, age, and city fields
* to the quicksearch tableRefactoring Legacy Applications Using CakePHP 9
/
$quicksearch_data = array(
‘username’ => $this->data[ ‘User’ ]["username’],
“gender’ => $this->data[ "User ][gender],
‘age’ => $this-rdata["User"][“age"],
“city’ => $this-rdata[ ‘User "][‘city"]
3
$this->QuickSearch->save($quicksearch_data) ;
}
The afterSave() method accepts a single parameter, a boolean field that tells you whether a new
record has been created or not. Obviously, this is not being used in our example. What we're doing
here is quite simple, really. We're grabbing a subset of the data that we saved in our User model and
then passing that set to our QuickSearch model for it to be updated. In this example, username’ is the
primary key for our quick search table. Unlike the beforeSave() method, we do not have to return any
value.
Removing Cached Data Upon Record Deletion
While caching is a great idea for just about application any application where you are expecting to
have more than one concurrent user, managing the cache is key to proper use Whenever you delete
data from one of your database tables, if you've cached that information anywhere you will want to
remove it from the cache. In CakePHP an ideal place for the logic to handle this would be in the be-
foreDelete() and afterDelete( callbacks
In this example, let's assume that we have written our own caching library, called crudeache, that we
have placed in your APP/vendors directory. It supports CRUD (Create, Read, Update, Delete) meth-
ods for talking to our cache
class User extends Appiiodel {
// Assume that we have set $this->user_id prior to deleting
public method afterDelete() {
App: simport(*Vendor", ‘“crudcache*) ;
$crudcache = ClassRegistry: :init(array( ‘class’ => ‘Crudcache"));
$cache_key = md5(‘quicksearch_’ . $this->user_id);
if (1$crudcache->delete($cache_key)) {10 Refactoring Legacy Applications Using CakePHP
$crudcache->1og(
‘unable to remove quicksearch information for user’
= $this-ruser_id);
}
// Example of using this, assuming we have the id of the record
$this->User->user_id = $user_id;
$this->User->delete(suser_id) ;
‘Your afterDelete( callback does not need to return anything, so any information about what's hap-
pened needs to be communicated through a different channel. In this simple example we use a log-
ging facility built into the CrudCache library itself.
Ifyou really wanted to do things the Cake-approved way you could create your own cache engine,
which is an exercise I leave to more motivated users as current documentation on that is sparse. If
you do go that route, your Model: :delete and Model::del methods will call Model::_clearCache for
you. Go with whatever you feel comfortable doing
Making Your Models Behave: Behaviors
One of the interesting features of CakePHP is that you can create code to improve the functionality of
your models. These bits of code are called behaviors, and they can give you very powerful functional-
Other behaviors that Ihave come across include things like taking an image that you have just created
arecord for, and automatically create thumbnails of different sizes, and adding in geocoding function-
ality to your application.
I think that any time you have some business logic that needs to do something at the same time you
are saving information in your database, a behavior is the way to go. This project does not require any
custom behaviors, but it's definitely something to investigate if you have some functionality that does
not appear to properly fit anywhere.
Request Routing
CakePHP has very powerful routing capabilities that are also surprisingly easy to use. Often you will
be faced with URL's in a legacy application that you cannot simply rename For example, let’s take
a look at a URL from a project I worked on where I was porting over a web service and we had to
preserve the URL's
One sample URL was /en/nba/games/¥ YY YMMDDXZ xml. To remain flexible, I decided to imple-Refactoring Legacy Applications Using CakePHP = 11
ment a ‘games’ controller, where we would pass in the league (‘nba’ in this case) and then the rest of
the information. So, how would we do this?
Router
“index'), array( ‘pass
‘onnect(' /en/:League/games/:event_key’, array("controller*
=> array(‘league’, ‘event_key")));
“games*, ‘action’ =>
So what is going on here? Okay, we can put “placeholders” or “tokens” (call them whatever you
want) into a route. In this case I want to pass the league and the event key to my action in my con-
troller for further processing, It’s really that simple and it reads like plain English, if you ask me
One mistake that is sometimes made is the desire of some developers to create their controllers and
actions to give you pretty URL's in advance, Sometimes this is easy, sometimes is not. Given how
flexible CakePHP’s routing system is, I think it’s better to focus on functionality first and worry about
the URL's second. You might even find that you have one controller powering multiple seemingly-
unrelated URL's12 Refactoring Legacy Applications Using CakePHP
Chapter 2 - Creating Controllers
Determining Controller Names
Now that we've defined our models, the next step is to define the controllers that we will need. In
CakePHP, the convention is that your controller maps to a noun (in most cases, a model) and your
controller methods are verbs that signify an action you're trying to do. However, in many legacy
applications this type of easy mapping is not possible, so you often have to struggle to match the
conventions.
Once I did a blog post about CakePHP myths, focusing on trying to dispel some of the more common
“urban legends" as it were about the framework. One of the bigger ones was the idea that you can
only have on Model associated with one Controller This is, of course, false. Once you realize that,
you understand that you are actually free to name your controllers and their associated actions what-
ever you want,
Often you are not lucky enough that your existing URL's will map easily to the noun/verb pattem.
That leaves you with taking a look at the existing URL's and doing a little educated guessing, Let's try
that here
URL. controller/action Functionality
1 Ipagefnome ‘Main Menu
Froster_management php rosters Roster management options
fmake_a_trade php rosters/trade Trade a player
‘Imodify_roster php rosters/edit Edit existing roster
‘Hfree_agents php free_agents Free agent management options
‘Hfree_agents php?task=sign ‘free_agents/sign Signa free agent
‘/free_agents plp?task—add Iplayeriadd ‘Adda new player
‘free_agents plp?task—view free_agentsiview View existing free agents
So far we've done a good job of mapping the old URL's to potential controller / action pairs. But per-
haps we can do better?
Free agents are simply a type of player, so maybe we could rewrite those actions dealing with free
agents to fall under the players controller Now, we have the rosters controller to deal with actions
involving rosters and the players controller dealing with players. Imagine that
Also, I think it makes more sense to put all the actions dealing with trades under their own controller,
as the make_a trade php script is it's own "front controller" (as you'll see later),
Let's not forget that we have some internal actions to account for When making a trade we have a
screen where we not only pick what teams are involved in a trade, but then you pick the players. So
we need to tweak things. Here's the final mapping after digging around.Refactoring Legacy Applications Using CakePHP 13
URL. controller/action Functionality
1 Ipagefnome ‘Main Menu
‘roster_management php rosters Roster Management
fmake_a_trade php rade ‘Make a trade, pick teams
fmake_a_trade php (after post) hrade/choose_players Make a trade, pick players
‘Imodify_roster php Irosters/choose ‘Modify rosters, pick team
‘Fmodify_roster php (after post) rosters/edit Modify rosters, edit players
‘Hfree_agents php free_agents ‘Manage free agents
‘free_agents php?task=sign free_agents/sign Modify free agents
Jdraft_player php players/draft Draft free agents
‘free_agents php?taske-add Iplayersfadd ‘Manage free agents, add player
‘free_agents plp?task—view free_agents/edit View free agents, edit
‘Wwiew transactions php ransactions View transactions, pick dates
‘Wview_transactions php (after post) ransactions/view ‘View transactions, show
‘Wwiew_rosters php rostershview View rosters
‘Wwiew_free_agents php ‘free_agentsiview View free agents
There, doesn't that look better now? We now have three controllers that better map to the actual ac-
tions being done, with a manageable number of methods in each controller I made the non-intuitive
decision to make the home page for the application a static page. The page will never change so there
really isno reason to make it dynamic
So let's set about creating the skeletons for our new controllers.
class RostersController extends AppController {
public function index() {
}
public function choose() {
}
public function edit() {
}
public function view() {
}14 Refactoring Legacy Applications Using CakePHP
class PlayersController extends AppController {
public function add() {
}
public function draft() {
}
}
class TradeController extends AppController {
public function index() {
}
public function choose _players() {
}
}
class FreedgentsController extends AppController {
public function index() {
}
public function sign() {
}
public function edit() {
}
public function view() {
}
}
class TransactionsController extends AppController {
public function index() {
}
public function view() {
}Refactoring Legacy Applications Using CakePHP 15
Linking models to controllers
Since we are only dealing with three models, and a fairly small data set (no more than about 1300
entries in total) we are not taking much of a memory hit to ask CakePHP to hold those three mod-
els in memory for us. In some instances, you are bound to have 10 to 12 models, so often it's better
to simply leave the Suses array empty and then instantiate instances of the models you need using
App-umportO,
class PlayersController extends AppController {
public function draft() {
$player = ClassRegistry: :init( ‘Player?);
}
If you wish to follow the path of least-use-of-resources, I recommend using the ClassRegistry method
but you really need to experiment with your application to see if it is worth it for you
Ifyou look at the two controllers you will see that we are passing some variables into some of the
action methods. This is the usual CakePHP convention when editing or viewing data in your standard
CRUD application, so there's no need to reinvent the wheel. I've said it before, and I will say it again
sticking to the CakePHP conventions as much as possible reduces the amount of work you have to do
Isnt that what we're really trying to accomplish here?
Components for your controllers
Much like you have behaviors to add and extend functionality for your models, components allow
you to add and extend functionality in your controller. The most common component I have used is
the Auth component, which allows you to add authorization code to your application. In fact, I've
written quite a few tutorials dealing with the Auth component. For WebReg, I'm not so sure I need to
use it, Right now the application is simply protected by Apache http-auth. For most applications, the
collection of core components is more than enough16 Refactoring Legacy Applications Using CakePHP
Chapter 3 - Layouts
CakePHP supports the concept of having layouts, meaning a skeleton for your HTML output and then
it will grab the output generated by the views and put it in the proper place, CakePHP comes with
a pretty decent default layout for HTML, so welll take that and simply modify it for our needs. We
would be saving this file in APP/views/layouts/default ctp
= $html->charset() >>
<2= $scripts_for_layout ?>
= $content_for_layout ?>
Being lazy I've started using PHP short tags in my applications, hence the use of =... ?> in the tem-
plates. Less typing, just as clear Believe me, when you work on converting over 100+ views on a site
you will appreciate the keystrokes saved
Setting Character Sets
So what exactly is going on here? First off, we are setting the character set for the layout, which
CakePHP defaults to UTF-8. You can set that to other values by passing it as a parameter
<2= $html->charset(*IS0-8859-1") ?>
I can't imagine too often that you'd be setting that value dynamically in your controllers, so go into
your layout files and just set it, and forgetRefactoring Legacy Applications Using CakePHP 17
Setting Page Titles
Stitle_for_layout is a placeholder for what you would like to place in your tag You can set the
value you want to show up here in a couple of different ways
You can give a particular controller / action pair a name when you create a route. For WebReg, we
have set this value to ‘home' in APP/configiroutes php . See how the display method in the standard
Pages controller does it?
public function display() {
$path = func_get_args();
$count = count ($path) ;
if ($count) {
$this-rredirect(*/");
}
Spage = $subpage = $title = null;
if (lempty(Spath[o])) {
$page = $path[0];
}
if (tempty(Spath[1])) {
$subpage = $path[1];
}
if (tempty($path[$count - 1])) {
$title = Inflector: :humanize($path[$count - 1]);
}
// For the home page we have to set the title
if ($path[$count - 1]
*home") {
$title = ‘WebReg Home ;
}
$this->set(compact(‘page’, ‘subpage’, ‘title’));
$this->render(join('/*, $path));18 Refactoring Legacy Applications Using CakePHP
The most common place to set this is in the controllers on a per-action basis.
class RostersController extends AppController {
public function index() {
$this->pageTitle = ‘Rosters Nain Page’;
}
Javascript Usage
Now, I imagine most applications will be using some sort of Javascript. In the "Web 2.0" environ-
ment it's pretty hard to avoid using Javascript for AJAX and other functionality (rounded comers
immediately springs to mind) so CakePHP provides you with an easy way to insert calls to load your
Javascript into your HTML output up in the .. block. When you use the Javascript
helper, set the "inline" parameter to be FALSE to tell the helper you don't want the Javascript link to
be mline
// this code is inside a view
<2= $javascript->Link( "foo", false) 2>
<1-- you should see the following in the header
Actual Content
Finally, we have the $content_for_layout variable, which is where CakePHP will place the HTML
output that your controller / action pairs generate. Not too many options for you here, but without that
$content_for_layout your layouts will, well, not display any content
Now that we've looked at the layout we'll be using for WebReg, there are some other things we would
probably want to use when refactoring
css
CakePHP makes it super-easy to add CSS files to your layouts.
// In your layout
= $html->css(‘main’) >>
From Everand
The Subtle Art of Not Giving a F*ck: A Counterintuitive Approach to Living a Good Life