Download as pdf or txt
Download as pdf or txt
You are on page 1of 66

Super Awesome Advanced CakePHP Tips

This book is available for free at http://www.pseudocoder.com/free-cakephp-book Version 1.1

By Matt urry pseudocoder.com twitter.com/mcurry matt!pseudocoder.com "ith ontributions #rom Mark $tory mark-story.com twitter.com/mark%story

$uper &wesome &dvanced ake'(' Tips by Matt urry is licensed under a reative &ttribution-)oncommercial-$hare &like *.+ ,nited $tates -icense. ake'(' is a re.istered trademark of the ake $oftware #oundation /http://cakefoundation.or.0

ommons

Contents
Who Should Read This Book..........................................................................................................................6
How to Read This Book.............................................................................................................................................6

How to Learn CakePHP..................................................................................................................................7


The Paid Way.............................................................................................................................................................7 The Free Way.............................................................................................................................................................8 A ter you!"e done #ither$Both % The A&o"e.........................................................................................................8

'odels...............................................................................................................................................................(
Re)ursion....................................................................................................................................................................( Containa&le Beha"ior................................................................................................................................................( Why *ou Should +se ,t.............................................................................................................................................( This Will Cat)h #"eryone At Least %n)e..............................................................................................................-.

Custo/ Find Ty0es........................................................................................................................................-The +no i)ial Cake Way........................................................................................................................................-'y Way....................................................................................................................................................................-1 Co/0arison..............................................................................................................................................................-2 A00 'odel................................................................................................................................................................-3

4ettin5 the Lo55ed ,n +ser ro/ Anywhere...............................................................................................-6


The +ser 'odel .......................................................................................................................................................-6 ,n The A00Controller..............................................................................................................................................-7 Ba)k to the +ser 'odel...........................................................................................................................................-7 +sa5e.........................................................................................................................................................................-8 What A&out The Con i5ure Class6........................................................................................................................-8 Full Sour)e...............................................................................................................................................................-(

Auto/ati)ally Tra)kin5 Created$'odi ied By............................................................................................1.


7ata&ase...................................................................................................................................................................1. 'odel Relations.......................................................................................................................................................1. 'odel &e ore8alidate Call&a)k..............................................................................................................................1. Beha"ior &e ore8alidate Call&a)k.........................................................................................................................1. The Full Beha"ior....................................................................................................................................................1-

Routin5............................................................................................................................................................11
Case ,nsensiti"e........................................................................................................................................................11

+nit Testin5....................................................................................................................................................19
8iews.........................................................................................................................................................................19

Setting Up The Files..................................................................................................................................23 Setting Up The Test Class.........................................................................................................................23 Standard Index View.................................................................................................................................24 Creating The View Test.............................................................................................................................25 Testing the Rendered View.......................................................................................................................26 Controllers.................................................................................................................................................28 oing Things the !ard "a#......................................................................................................................28 Testing $ Controller %ethod....................................................................................................................3& %a'ing assertions......................................................................................................................................3(
'o)k %&:e)ts...........................................................................................................................................................99

"hat is a %o)' *+,e)t..............................................................................................................................33 "here )an I get one o- these -a+.lo.s de/i)es0........................................................................................33 %a'ings expe)tations with %o)' *+,e)ts................................................................................................34
'odels......................................................................................................................................................................96

Test Case...................................................................................................................................................36 Fixt.res......................................................................................................................................................36 'er5in5 Add and #dit A)tions.....................................................................................................................97


The Controller.........................................................................................................................................................97 Tellin5 Add And #dit A0art...................................................................................................................................98 The 8iew...................................................................................................................................................................9(

Cake Tri)ks ro/ The Core..........................................................................................................................2.


Cake Style ;o0tions Para/eter...............................................................................................................................2. Handlin5 7ata Arrays with a Sin5le Re)ord or an Array o Re)ords.................................................................2-

Stu0id #asy +RL Slu5s..................................................................................................................................29


'akin5 The Slu5s A Bit S/arter...........................................................................................................................22

Changing The Unders)ore To $ !#phen..................................................................................................44 Consisten)# $nd S1* I2pro/e2ents.......................................................................................................44 :<uery..............................................................................................................................................................23


Re0la)in5 ;:a"as)ri0t=>e"ent?@...............................................................................................................................23 Re0la)in5 ;a:aA=>link?@...........................................................................................................................................23

#A0andin5 Trees With :<uery......................................................................................................................27


Basi) Tree.................................................................................................................................................................27 TreeHel0er...............................................................................................................................................................27 TreeHel0er With :<uery ........................................................................................................................................3. Cleanin5 +0 the ,/a5es..........................................................................................................................................31

Ba"aS)ri0t ,n 8iews.......................................................................................................................................39 'ake *our Cake A00 Fast...........................................................................................................................32


7onCt +se ;uses +nless *ou ReallyD A&solutely Ha"e To......................................................................................32

%odel Chains.............................................................................................................................................54 Controller33load%odel and ClassRegistr#33init..........................................................................................54


+se Containa&le.......................................................................................................................................................33 Set 7e&u5 to ...........................................................................................................................................................33

Ca)he your slow Eueries$we& ser"i)e reEuests$whate"er......................................................................................33 8iew Ca)hin5...........................................................................................................................................................33 HT'L Ca)hin5........................................................................................................................................................36 APC ?or so/e other o0)ode )a)he@.........................................................................................................................36 Persistent 'odels.....................................................................................................................................................36 Store The Persistent Ca)he in APC........................................................................................................................37 S0eed +0 Re"erse Routin5......................................................................................................................................37 +n)hain *our 'odels..............................................................................................................................................37

The 4iant Con i5urationD 8ersion Control and 7e0loy/ent Se)tion.......................................................3(


8ersion Control.......................................................................................................................................................3(

)ore.php.....................................................................................................................................................54 +ootstrap.php.............................................................................................................................................54 data+ase.php..............................................................................................................................................6&


'ulti0le #n"iron/ents...........................................................................................................................................67e0loy/ent..............................................................................................................................................................6-

e+.g........................................................................................................................................................6( Ca)he.........................................................................................................................................................6(
Alternate 'ethods...................................................................................................................................................6-

CakePHP Reser"ed Classes...........................................................................................................................61 Fro/ The Bakery ?And %ther Pla)es@.........................................................................................................69


Beha"iors .................................................................................................................................................................69

Sl.gga+le ..................................................................................................................................................63 So-t eleta+le ...........................................................................................................................................63 5in'a+le ....................................................................................................................................................63


Plu5ins .....................................................................................................................................................................69

e+.g6it ..................................................................................................................................................63 7a2edS)ope..............................................................................................................................................63


Hel0ers .....................................................................................................................................................................62

$sset..........................................................................................................................................................64 89.er# Validation.......................................................................................................................................64 !t2lCa)he.................................................................................................................................................64 Co0yri5ht........................................................................................................................................................63 Re"isions..........................................................................................................................................................66


8-.. F 'ay -9D 1..(.................................................................................................................................................66 8-.- F 'ay -3D 1..(.................................................................................................................................................66 8-.1 F Bune -1D 1..(................................................................................................................................................66

Who Should Read This Book


This book isn4t meant for people wantin. to learn ake'('. There are already plenty of resources for that. 5f you4re completely new to 6 ake6 /that4s what we in-the-know call it0 check out the section below7 which outlines two paths for .ettin. started. 5 will make several assumptions about you7 the reader7 in this e-book. 8ou have built a ake'(' app before or at least are familiar with the blo. tutorial. 8ou want to improve your ake'(' skills. ake'(' alone does not ma.ically make you write better code. 54ve seen plenty of shitty code in ake'(' apps. 8ou understand that this book is free and 5 will probably mess up parts and other parts will become out of date. 8ou will want to help me fi9 this by sendin. me an email at matt!pseudocoder.com.

How to Read This Book


This book doesn:t have to be read cover-to-cover. The information7 .enerally7 doesn:t rely on what you:ve learned in previous sections. #eel free to ;ump around or skip directly to sections that interest you. Think of it as a series of blo. posts in book form.

<

How to Learn CakePHP


The Paid Way
There are a couple ake'(' books out ri.ht now. The bi..est caveat with all of them is that they were written before the stable release of ake'(' 1.17 so there may be discrepancies with the most recent version of the framework.
Beginning CakePHP: From Novice to Professional

by =avid >oldin. &vailable at &ma?on.com for $28.37

CakePHP Application Development

$tep-by-step introduction to rapid web development usin. the open-source MV ake'(' by &hsanul Bari and &nupom $yam !aila"le at #a$on.%o# &or $3'.((

Practical CakePHP Projects

by @ai

han and Aohn Bmokore with Cichard Miller

!aila"le at #a$on.%o# &or $28.37

The )ree Way


>o to The ake'(' 1. hapter 1 1. *. 2. The 5ntro 1.1 "hat is ake'('E "hy ,se itE 1.* ,nderstandin. Model-View- ontroller 1.*.1 Benefits &ll of it7 includin. any subsections and subsubsections *.1 CeFuirements *.1 5nstallation 'reparation *.1.1 >ettin. *.* 5nstallation *.*.1 =evelopment $ample Blo. &pp - =o The "(B-G Thin. ake'(' *.1.1 'ermissions ookbook and read the followin. sections in this order:

hapter 1 hapter *

hapter 1+.1

&ter you*!e done +ither,Both -& The "o!e


&t this point you should have a pretty .ood understandin. of the basics of ake'('. #eel free to investi.ate the rest of the ookbook7 check out some other resources or finish this book. &lso the book HCefactorin. -e.acy &pplications ,sin. ake'('I by hris (art;es is a .reat resource for only J1+ /5 reviewed it here0. The book provides .reat7 practical e9amples showin. how procedural7 clusterfucked '(' code can be rewritten in the ake #ramework. My recommendation would be come up with an app you want to build. 5t doesn4t have to be anythin. .reat and can simply be a clone of somethin. else that4s already out there. Then build it. 8ou4ll encounter different roadblocks that will force you to learn more about the framework. &t each roadblock take a look on the ookbook or >oo.le and see if there is a best way to accomplish want you want. 5f you can4t fi.ure out why somethin. isn4t workin. try takin. a look in the framework code. 5t can be intimidatin. at first7 but it4s well coded and you4ll learn a lot more than if you ;ust post a Fuestion and .et a Fuick answer. 5 can4t recommend this hi.hly enou.h: >et remote debu..in. workin. />oo.le 69debu.60. The ability to put break points in your code7 then step throu.h it line by line7 with the power to inspect and chan.e variables7 is a revelation. 5t4s like doin. echo 'here'; die; on steroids7 (>( and speed. K

.odels
Re%ursion
The recursive property in ake'(' models is used when determinin. how much information is returned with your Fueries. The problem is that it .ives you only limited control. 8ou can select the level of information you want7 but if you have many related tables you will often retrieve more information than you need7 which will result in wasted Fueries. (ere are some si.ns you have recursion set too hi.h in your app:
: : :

"hen you4re lookin. at the outputted Fueries with debu. on you4ve reached the cap /1++0 that will be displayed. 8our pa.e takes so lon. to load that you have enou.h time to benchmark all the ma;or '(' frameworks and post about it on your blo.. 8ou4re usin. a recursive level that isn4t -1.

)ow you4re confused7 ri.htE The first thin. 5 do with any new ake'(' app is create an &ppModel and set var $recursive = -1; (ow do you retrieve related tablesE Cead on...

Containa"le Beha!ior
The containable behavior .ives you precise control over what data is returned. This means you won4t waste Fueries loadin. related tables that you may not care about. The ake'(' Book e9plains it pretty well7 so 5 won4t waste space .oin. over the same material. 5nstead 54ll emphasi?e ;ust how important it is to use.

Why /ou Should 0se 1t


8ou may be thinkin. 6My app is pretty small. ,sin. ontainable would be overkill.6 &nd maybe you4re ri.ht. Maybe you ;ust have a 'ost model that has a belon.sTo association to a ,ser model. 5t4s easy enou.h for you to set Jrecursive to -17 + or 1 as needed. $o you do this. &ll over your app. Then a few months later you add a Ta. model and $tat model and who knows what else7 but they4re all related to 'ost. )ow all of a sudden you4re .ettin. back all this e9tra information you don4t need7 when you really ;ust want ,ser. 5f you use ontainable you never have to worry about new model associations blowin. up your e9istin. Fueries.

This Will Cat%h +!eryone t Least -n%e


$o you4ve set Jrecursive to -1 in the &ppModel and are happily containin. your related models. 8ou fire up a standard inde9 with the built in pa.inator: function index() { $this->Post->recursive = 0; $this->set('posts', $this->paginate());

,.h. 5t has that u.ly call to set recursive to +. $o you delete it7 but you still have to load the ,ser model to show who created each post. $o you make the code look like this: function index() { $this->Post->contain('!ser'); $this->set('posts', $this->paginate());

"hen you refresh the pa.e it4s broken. The 'ost data loaded7 but the associated ,ser model wasn4t. -ookin. closer you see that the count Fuery was fine: "#$#%& %'!(&()) *" +count+ ,-'. +posts+ *" +Post+ $#,& /'0( +users+ *" +!ser+ '( (+Post+1+2odified3user3id+ = +!ser+1+id+) 45#-# 1 = 1 But the second Fuery7 the one that loads the actual data7 is messed up - there is no ;oin to the users table. "#$#%& +Post+1+id+, +Post+1+tit6e+, +Post+1+7od8+ ,-'. +posts+ *" +Post+ 45#-# 1 = 1 $0.0& 90 The issue is that even thou.h you are containin. the ,ser model7 it is bein. reset after the first Fuery7 the count Fuery. $o when the second Fuery runs7 the one to .et the actual data7 it doesn4t know anythin. about the bindin.. There is a simple fi9 for this7 and it4s mentioned in the manual7 but it4s .oin. to come up if you force recursive to always be -17 so it4s worth statin. a.ain. 8ou can set the controller:s pa.inate options to contain any related models you want before you call $this->paginate(). function index() { $this->paginate = arra8('contain' => '!ser'); $this->set('posts', $this->paginate());

1+

Custo# )ind Ty2es


8ou4ve been writin. some pretty borin. apps if you haven4t had to use the built in ake'(' find types to retrieve some data. Gither that or you4ve ;ust been usin. $.ode6->:uer8 and writin. custom $M- all the time /54ve seen this0. Br you4ve been makin. your own database connection with the standard '(' My$M- functions and used mysFl%Fuery /yes7 54ve seen this in a ake'(' app - serious facepalm0. 8ou4re probably familiar with the types 6first6 and 6all6 at a bare minimum. "ouldn4t it be cool to create your own typesE -ike if you had a model 'ost and wanted to do $Post->find('6atest') or $Post->find('tag', arra8('php', '7eer', 'nsf;')). 8ou4ll find a bunch of different ways to do this around the web. 54ll e9plain two different ways to accomplish this. 'ersonally 5 like my method described below /the second one0. 8ou could probably make an ar.ument that some of the other ways are better7 but if 5 don4t .et my way 54ll take my e-book and .o home.

The 0no&&i%ial Cake Way


ake4s base Model class has an attribute defined that keeps track of all the default find types. 5t looks like this: var $3find.ethods = arra8( 'a66' => true, 'first' => true, 'count' => true, 'neigh7ors' => true, '6ist' => true, 'threaded' => true ); To create your custom find type you need to add it to this list. 5f your custom find type was HlatestI you would do somethin. like this: c6ass Post.ode6 extends *pp.ode6 { $this->3find.ethods = arra8('6atest' => true);

)ow you have to create the method that will be called. The method name must match the pattern 3find%usto2&8pe. 5n the e9ample about it would be 3find$atest. The tricky part is that this method will be called twice. Bnce before the database Fuery is made and once after. function 3find$atest($state, $:uer8, $resu6ts = arra8()) { if ($state == '7efore') { $:uer8<'6i2it'= = 10; $:uer8<'order'= = 'created >#"%'; return $:uer8; e6seif ($state == 'after') { return $resu6ts;

11

5n the before state you will have access to the $:uer8 array7 where you can set conditions7 limit7 order...all the usual find options. 5n the after state the $resu6ts array is passed. 8ou can then alter $resu6ts to fit whatever you4re tryin. to do or ;ust return it directly.

.y Way
My way reFuires a bit more initial setup7 but really it4s ;ust a matter of copyin. and pastin. some code to your &ppModel and you don4t have to add anythin. for each new find method. 54ll step throu.h what it does7 then include the entire block at the end. #irst in your &ppModel you4ll need to override the find function. function find($t8pe, $options = arra8()) {

5n the actual model class /not &ppModel0 we will create methods for each find type that uses the Jtype parameter as a piece of the method name. $2ethod = sprintf('33find?s', 0nf6ector@@ca2e6iAe($t8pe)); #or e9ample callin. $Post->find('6atest') will look for the method 6%%find-atest6 in the 'ost model class. 5f you can4t come up with snappy names for your find types and end up with $Post>find('6atest3;ith3co22ents') the private method will be 6%%find-atest"ith omments6. Then it is simply a matter of checkin. if the method e9ists and callin. it. Br if it doesn4t e9ist7 callin. the parent. if(2ethod3exists($this, $2ethod)) { return $this->{$2ethod ($options); e6se { return parent@@find($t8pe, $options);

There is one catch. ake will sometimes call find internally and pass an array as the Jtype parameter. To catch that simply check if Jtype is a strin. before settin. Jmethod.

11

The whole thin34 /also available as a plu.in on >it(ub0 function find($t8pe, $options = arra8()) { $2ethod = nu66; if(is3string($t8pe)) { $2ethod = sprintf('33find?s', 0nf6ector@@ca2e6iAe($t8pe)); if($2ethod BB 2ethod3exists($this, $2ethod)) { return $this->{$2ethod ($options); e6se { $args = func3get3args(); return ca663user3func3arra8(arra8('parent', 'find'), $args);

)ow you can create a method in your 'ost model like: function 33find$atest($options) { $options = a2(arra8('conditions' => arra8('pu76ished' => true), 'order' => arra8('created' => 'desc'), '6i2it' => 10 ), $options ); return parent@@find('a66', $options);

Then you can simply call: $Post->find('6atest');

1*

Co#2arison
There are advanta.es and disadvanta.es to each method. The ,nofficial ake "ay: 5 -ess initial code 5 &ccess to the before and after states 6 (ave to handle before and after states My "ay: 5 =on4t have to override the Model %%constructor and setup each custom find type. Aust write the method. 6 $ome initial setup. 5 =on4t have to handle before and after states. 54ll e9plain that last one. "ith My "ay you have access to both states7 but don4t have to e9plicitly handle them. #or e9ample what if your custom find type was really ;ust a wrapper to one of the default find types. -et4s say you wanted to .et a count of comments on your blo. and double it so you looked popular. "ith My "ay you4d simply do: function 33find%o22ent%ount($options) { return $this->find('count')) ) 9;

=oesn4t .et much simpler than that. (ere4s the same thin. with the ,noffical

ake "ay:

function 3find%o22ent%ount($state, $:uer8, $resu6ts = arra8()) { if ($state == '7efore') { return $this->3find%ount($state, $:uer8, $resu6ts); e6se { return $resu6ts ) 9;

12

22 .odel
8ou can pull to.ether all of the above to create a base &ppModel suitable for any new app you start. CDphp c6ass *pp.ode6 extends .ode6 { var $acts*s = arra8('%ontaina76e'); var $recursive = -1; function find($t8pe, $options = arra8()) { $2ethod = nu66; if(is3string($t8pe)) { $2ethod = sprintf('33find?s', 0nf6ector@@ca2e6iAe($t8pe)); if($2ethod BB 2ethod3exists($this, $2ethod)) { return $this->{$2ethod ($options); e6se { $args = func3get3args(); return ca663user3func3arra8(arra8('parent', 'find'), $args);

D>

13

7ettin3 the Lo33ed 1n 0ser &ro# nywhere


This is a problem that comes up often. >enerally you need access to the session to retrieve information about the lo..ed in user. 5n the view this is done with the session helper. $session->read('*uth1!ser1id'); 5n the controller you can use the $ession omponent. $this->"ession->read('*uth1!ser1id'); But what if you have a model where you need to set a modified user field every time you saveE 5t would be .reat if you could handle this automatically in the before#ilter. 8ou can always cheat and use the J%$G$$5B) super.lobal7 but that4s not very akey. 'lus7 now we are usin. three different methods dependin. on the part of the app we4re in. (ow awesome would it be if you could ;ust call !ser@@get('id') from anywhereE Bn a scale of +N-1++N7 that4s like K1N awesome7 ri.htE

The 0ser .odel


(ere4s how to do it. #irst you need a ,ser model7 which you probably already have. This code is a little misplaced amon. the rest of the ,ser model code7 but for the !ser@@get synta9 to work it has to .o in the model. =eal with it. The first thin. you4ll need is a way to .et a sin.leton instance of your user instance. function Bget0nstance($user=nu66) { static $instance = arra8(); if ($user) { $instance<0= =B $user; if (E$instance) { trigger3error(33(F!ser not set1F, true), #3!"#-34*-(0(G); return fa6se; return $instance<0=;

This method will be used internally to store and retrieve the user4s information. =on:t worry if you don:t understand what:s .oin. on here. Aust trust that it works.

1<

Before you can access the user info you need to store the user in the static instance. The method 6set6 is already taken by ake4s base Model class so we4ll use 6store6. The store method is very simple: function store($user) { !ser@@get0nstance($user);

1n The 22Controller
The ,ser::store method is used in the &pp ontroller Before#ilter. *pp@@i2port('.ode6', '!ser'); !ser@@store($this->*uth->user()); The lo..ed in user is pulled from the &uth omponent and passed as a parameter to the store method. 5f you4re not usin. the &uth omponent you can substitute whatever half-assed7 cobbledto.ether authentication system you4re usin.. 8ou ;ust need to pass in the user information as an array.

Ba%k to the 0ser .odel


The !ser@@get method can then be used to .et the instance and pull out a specific piece of information from the user array. function get($path) { $3user =B !ser@@get0nstance(); $path = str3rep6ace('1', 'H', $path); if (strpos($path, '!ser') E== 0) { $path = sprintf('!serH?s', $path); if (strpos($path, 'H') E== 0) { $path = sprintf('H?s', $path); $va6ue = "et@@extract($path, $3user); if (E$va6ue) { return fa6se; return $va6ue<0=;

1D

0sa3e
To .et the currently lo..ed in user:s id: !ser@@get('id'); To .et the currently lo..ed in user:s username /assumin. you have a field HusernameI in your users table0: !ser@@get('userna2e'); &ny other fields in your user table can be retrieved in the same manner. &lso if you store related model data in your user session it can be retrieved: !ser@@get('.ode61fie6dna2e');

What "out The Con&i3ure Class8


$ome of this code will look familiar to those of you who have du. around in the ake core. That4s because it4s blatantly stolen inspired by the onfi.ure class. Bbviously7 you could skip all the user setup and ;ust use onfi.ure like this: HHin the *pp%ontro66er %onfigure@@;rite('!ser', $this->*uth->user()); HHfro2 an8;here e6se %onfigure@@read('!ser1id');

5t4s not awful7 but the synta9 isn4t as appealin.. But7 wait. "hy not ;ust use the same setup for ,ser7 but have it wrap calls to onfi.ure. Then ,ser doesn4t have to bother .ettin. the static instance of itself and you .et the cooler !ser@@get() synta9. &.ain7 certainly an option. 5 wouldn4t hold it a.ainst you if you did it that way. (owever stickin. the lo..ed in user in the onfi.ure class is a bit misplaced. 5t4s not Hend of the worldI bad to do it this way. More like Hshit7 5 ;ust dropped my i'od in the toiletI bad. Ceally it4s a personal decision that each developer will need to search deep within. their pro.rammer souls to find the solution that fits them best.

1K

)ull Sour%e
(ere is the full source7 which is also available on >it(ub. function Bget0nstance($user=nu66) { static $instance = arra8(); if ($user) { $instance<0= =B $user; if (E$instance) { trigger3error(33(F!ser not set1F, true), #3!"#-34*-(0(G); return fa6se; return $instance<0=; function store($user) { if (e2pt8($user)) { return fa6se; !ser@@get0nstance($user); function get($path) { $3user =B !ser@@get0nstance(); if (strpos($path, '!ser') E== 0) { $path = sprintf('!serH?s', $path); if (strpos($path, 'H') E== 0) { $path = sprintf('H?s', $path); $va6ue = "et@@extract($path, $3user); if (E$va6ue) { return fa6se; return $va6ue<0=;

1L

uto#ati%ally Tra%kin3 Created,.odi&ied By


5n the previous section we establish a method for retrievin. the lo..ed in user from anywhere in your application. )ow we can apply this to settin. the created by and modified by fields for various database records.

9ata"ase
#irst off7 add the fields 6created%user%id6 and 6modified%user%id67 both ints7 to any database table you want to track chan.es on.

.odel Relations
)e9t you need to set up a belon.sTo &ssociation for each of the fields. var $7e6ongs&o = arra8('%reated!ser' => arra8('c6ass(a2e' => '!ser'), '.odified!ser' => arra8('c6ass(a2e' => '!ser'));

.odel "e&ore:alidate Call"a%k


This is the point that most books would show you how to do a normal model beforeValidate callback7 while you4re thinkin. 6"hat a fri..in4 idiot. (e calls this e-book/e-pamphlet $uper &wesome &dvanced ake'(' Tips7 but then doesn4t know enou.h to make this into a behavior.6 Then in the ne9t section the author would make it into a behavior and you would feel mildly foolish7 but ;ustified in believin. the author should have ;ust skipped ri.ht to the behavior method. $o 5 won4t insult your intelli.ence and skip to the ri.ht way to handle this.

Beha!ior "e&ore:alidate Call"a%k


#irst let4s deal with the created by scenario. function 7eforeIa6idate(B$2ode6) { if(e2pt8($2ode6->data<$2ode6->a6ias=<'id'=)) { $2ode6->data<$2ode6->a6ias=<'created3user3id'= = !ser@@get('id'); return true;

1+

The lo.ic here is pretty simple. "e check if the 6id6 field is set to determine if this is a new record or an update. "e4re makin. a bit of a leap here7 in believin. that if the id is set it will be an update. 5t is certainly possible that an invalid id is passed. ake handles this by checkin. if the id e9ists first. 8ou4ll usually see a Fuery like this before an insert or update: "#$#%& %'!(&()) *" +count+ ,-'. +posts+ *" +Post+ 45#-# +Post+1+id+ = 1 $o there is the outside possibility that the created%user%id field may not .et set this way. 5f you wanted to be dili.ent you could move the lo.ic to an after$ave7 which is passed a Jcreated boolean. To set the modified%user%id you use basically the same code7 less the check to see if this is a new record. $2ode6->data<$2ode6->a6ias=<'2odified3user3id'= = !ser@@get('id');

The )ull Beha!ior


CDphp c6ass &racJa76eKehavior extends .ode6Kehavior { function 7eforeIa6idate(B$2ode6) { if (e2pt8($2ode6->data<$2ode6->a6ias=<'id'=)) { $2ode6->data<$2ode6->a6ias=<'created3user3id'= = !ser@@get('id'); $2ode6->data<$2ode6->a6ias=<'2odified3user3id'= = !ser@@get('id'); return true; D>

11

Routin3
Case 1nsensiti!e
>enerally 5nternet ,C-s are case insensitive. 8ou can put the address http://en.wikipedia.or./wiki/ ake'(' or http://en.wikipedia.or./wiki/cakephp in your browser and you4ll end up at the same place. Coutes in ake'(' are not case insensitive by default. #ortunately the routin. system uses re.ular e9pressions7 so it is easy to make a specific route work no matter how it is typed in. 5f you want to have an HaboutI pa.e that is linked to by H/aboutI7 H/&boutI7 or H/aBo,tI you can use: -outer@@connect('H(Di)a7out', arra8('contro66er' => 'pages', 'action' =>'disp6a8', 'a7out')); The 6/Ei06 part tells the re.ular e9pression en.ine to i.nore the case for all the te9t that follows. There is no way to apply this rule universally to all your routes7 so you4ll have to handle instances individually.

11

0nit Testin3
:iews
The ake'(' ookbook describes a way to test views usin. web testin.. This is different than unit testin. in that you actually make a reFuest to the web pa.e and check the html response. By doin. this there is no way to specify that the test database should be used7 therefore any data that is saved will .o in the HliveI database. This may not be a bi. deal if the HliveI database is ;ust your development environment. 5n addition7 since you are testin. the view by makin. a reFuest7 you aren:t isolatin. the view. The ake framework7 the controller and any models that are normally used in .eneratin. the output for the view will be used. This makes it hard to tell if an error is due to an issue with the view or from somewhere else. The method described isolated testin. to ;ust the view. )o models or controllers are needed as the view class is called directly. Because this method bypasses the controller7 any helpers will need to be included manually and any data needed by the view will need to be faked. Settin3 02 The )iles To setup a view test you:ll first need to create a directory HappHtestsHcasesHvie;s1 5n there put a file for each controller. The namin. convention Ccontro66er>3vie;1test1php works well. 5n this e9ample 5:ll be testin. the views for the 'ost controller/model7 so 5 created a file HappHtestsHcasesHvie;sHpost3vie;1test1php. The file itself is similar to the model or controller tests7 in that it will e9tend c6ass PostIie;&est%ase extends %aJe&est%ase { akeTest ase.

Settin3 02 The Test Class The first thin. needed is an instance of the View class. & .ood place to do this is in the startTest method. function start&est() { $%ontro66er = ne; %ontro66er(); $this->Iie; = ne; Iie;($%ontro66er); $this->Iie;->6a8out = nu66; $this->Iie;->vie;Path = 'posts';

The View class e9pects a controller to be passed as a parameter to the constructor7 so a base controller is created for this. The layout is set to null7 which allows the view to be rendered without the default layout. &lso the view'ath needs to be set to the folder in views that will be tested.

1*

To be safe we:ll be sure that the View was created successfully: function testPost0nstance() { $this->assert&rue(is3a($this->Iie;, 'Iie;'));

Standard 1nde; :iew 5f you use the ake'(' console to bake an inde9 pa.e it will look somethin. like this:

The pa.e consists of a title /'osts07 information about the number of records /pa.ination07 a table with a header row and the records themselves7 some more pa.ination and finally a new post link. To render this view ake used two helpers7 'a.ination and (TM-7 plus the data for the records.

12

Creatin3 The :iew Test )ow we can create the test for the inde9 pa.e. function testPost0ndex() {

Because a .eneric controller class was used7 the view has no knowled.e of which helpers are needed. They will need to be set manually. $this->Iie;->he6pers = arra8('5t26', 'Paginator'); The view e9pects certain pa.ination information to be set by the controller. #or this test that information will need to be set e9plicitly. $this->Iie;->para2s<'paging'= = arra8( 'Posts' => arra8 ( 'count' => 1, 'page%ount' => 1, 'page' => 1, 'current' => 1, 'prevPage' => fa6se, 'nextPage' => fa6se, 'options' => arra8('6i2it' => 90), 'defau6ts' => arra8() ) ); The records for 'ost also need be set so that the view has somethin. to display. $this->Iie;->vie;Iars<'posts'= = arra8 ( arra8 ( 'Post' => arra8 ( 'id' => 1, 'tit6e' => '$ore2 ipsu2 do6or sit a2et', '7od8' => '$ore2 ipsu2 do6or sit a2et111', 'created' => '900L-01-9M 1N@O1@P0', 'updated' => '900L-01-9M 1N@O1@P0' ) ) ); (ere we are ;ust settin. one record7 but you could certainly set as many as you:d like.

13

&ll that is left is to render the view and .et the output. $ht26 = $this->Iie;->render('index'); The strin. Oinde9: is passed7 specifyin. which view file to use. The rendered view7 not wrapped in the layout7 is returned and stored in $ht26. Testin3 the Rendered :iew &t this point the view has been setup and Iie;@@render() has been called7 returnin. some (TM-. The (TM- is a strin.7 so to run tests a.ainst it you can either use strin. parsin. or convert it to another format. 5f you wanted to verify some te9t appears in the view you could simply use '(':s strpos function. $this->assert(ot#:ua6(fa6se, strpos($ht26, 'Page 1 of 1, sho;ing 1 records out of 1 tota6, starting on record 1, ending on 1')); &nother option is to use ake:s PM- lib to parse the data. 5 like to further convert it to an array7 which allows me to use the $et lib to test various parts of the view. *pp@@i2port('%ore', 'Q26'); $Q26 = ne; Q26($ht26); $page = $Q26->first()->to*rra8(); 5t is now possible to check the number of rows in the table by doin.: $this->assert#:ua6(count("et@@extract('H&a76eH&r', $page)), 9); 5n this case there should be 1Q the header row and the one record. To test that the table headers are correct: $this->assert#:ua6("et@@extract('H&a76eH&rH&hHaHva6ue', $page), arra8('0d', '&it6e', 'Kod8', '%reated', '!pdated'));

1<

To check a specific field in a record: $this->assert#:ua6( tri2(arra83shift("et@@extract('H&a76eH&r<9=H&dHM', $page))), '900L-01-9M 1N@O1@P0' ); There:s a lot .oin. on in that one line7 so let:s work from the inside out. "et@@extract('H&a76eH&r<9=H&dHM', $page) This returns the *rd field from the 1nd row. $ince the 1st row is the header7 this is actually the 1st data record. tri2(arra83shift("et@@extract('H&a76eH&r<9=H&dHM', $page))) $et::e9tract actually returns an array7 but we ;ust want the first element7 which should be the only one. '(':s arra83shift pulls the one record for us. #inally7 the data needs to be trimmed because the view probably looks like this: Ctd> CDphp echo $post<'Post'=<'created'=; D> CHtd> )otice how the (TM- is nicely formatted. "e don:t care about all that spacin.7 so it is trimmed off to leave ;ust the value we want to test. &fter all that the final output should match the e9pected value7 41++L-+1-1* 1<:31:2+4 in this case. The full view test is available at http://.ithub.com/mcurry/cakephp/blob/master/test%sample/tests/cases/views/post%view.test.php

1D

Controllers This section contributed by Mark $tory. Bri.inally posted at http://markstory.com/posts/view/testin.-cakephp-controllers-the-hard-way By now you already know or should know about akeTest ase::test&ction/0 and the wondrous thin.s it can do. (owever7 test&ction has a few shortcomin.s. 5t can4t handle redirects7 it doesn4t let you use the power of Mocks7 and it4s impossible to make assertions on ob;ect state chan.es. $ometimes you need to do thin.s the hard way7 stick your fin.ers in the mud and work it out. (owever7 knowin. how to test a controller the old fashioned way takes a .ood knowled.e of ake'('. 9oin3 Thin3s the Hard Way ontrollers reFuire a number of callbacks to be called before they are workable ob;ects. $o let:s .o7 we4ll start with the venerable 'osts ontroller7 and make a nice test for it. *pp@@i2port('%ontro66er', 'Posts'); c6ass &estPosts%ontro66er extends Posts%ontro66er { var $na2e = 'Posts'; var $auto-ender = fa6se; function redirect($ur6, $status = nu66, $exit = true) { $this->redirect!r6 = $ur6; function render($action = nu66, $6a8out = nu66, $fi6e = nu66) { $this->rendered*ction = $action; function 3stop($status = 0) { $this->stopped = $status;

c6ass Posts%ontro66er&est%ase extends %aJe&est%ase { var $fixtures = arra8('app1post', 'app1co22ent', 'app1posts3tag', 'app1tag'); function start&est() {

function end&est() {

1K

$o we start off with a basic test class. 5mportant thin.s to notice are the fi9tures array and the test class. 54ve included all the fi9tures that are related to the models my controller is .oin. to use. This is important7 as you will .et tons of table errors until they are all setup. 8ou may have noticed that 5 created a subclass of the test sub;ectQ this lets me do a few thin.s. #irst 5 can test functions that call redirect()7 as they no lon.er redirect. 5 can also call methods that use $this->3stop() as they no lon.er halt script e9ecution. #urthermore7 5 override %ontro66er@@render() so 5 can test actions that use render() without havin. to deal with piles of (TM-. 5 personally don4t do many tests that assert the (TM- of my views because 5 find it takes too much time and is tedious. -astly7 5 set $auto-ender to false ;ust in case. function start&est() { $this->Posts = ne; &estPosts%ontro66er(); $this->Posts->construct%6asses(); $this->Posts->%o2ponent->initia6iAe($this->Posts); HHtests are going to go here1 function end&est() { unset($this->Posts); %6ass-egistr8@@f6ush();

"e then build the instance and call some basic callbacks7 much like =aniel blo..ed about. &t this point we have a controller instance and all the components and models built. "e are now ready to start doin. some testin..

1L

Testin3

Controller .ethod

Testin. a controller method is ;ust like testin. any other method. Bften there is a bit more setup involved as controllers reFuire more inputs by nature. (owever7 it is all achievable in the test suite. $o we are .oin. to do a test of our ad2in3edit method. This ad2in3edit is strai.ht out of bake7 so you should know what it looks like. #urthermore7 5 can show you how you can test methods. function test*d2in#dit() { $this->Posts->"ession->;rite('*uth1!ser', arra8( 'id' => 1, 'userna2e' => '2arJstor8', )); $this->Posts->data = arra8( 'Post' => arra8( 'id' => 9, 'tit6e' => 'Kest artic6e #varE', '7od8' => 'so2e text', ), '&ag' => arra8( '&ag' => arra8(1,9,M), ) );

&t this point 54ve created the inputs 5 need for my controller action. 54ve .ot a session and some test data. 54ve provided enou.h information in the session that &uth omponent will let me by and edit my records. (owever7 many would say that you should bypass &uth entirely in your unit testin. and ;ust focus on the sub;ect method. But bein. thorou.h never hurt. function test*d2in#dit() { $this->Posts->"ession->;rite('*uth1!ser', arra8( 'id' => 1, 'userna2e' => '2arJstor8', )); $this->Posts->data = arra8( 'Post' => arra8( 'id' => 9, 'tit6e' => 'Kest artic6e #varE', '7od8' => 'so2e text', ), '&ag' => arra8( '&ag' => arra8(1,9,M), ) ); $this->Posts->7efore,i6ter(); $this->Posts->%o2ponent->startup($this->Posts); $this->Posts->ad2in3edit();

*+

54ve now simulated most of a reFuest in ake'('. 5t is important to fire the callbacks in the correct order. Aust remember that before#ilter happens before Component::startup()7 and %o2ponent@@7efore-ender() happens after you call your controller action. .akin3 assertions "hen 5 test controllers 5 usually make assertions on the viewVars that are set and any records that are modified / deleted. 5 don4t like makin. assertions on the contents of $this->"ession>set,6ash() as 5 find these messa.es chan.e often which can lead to broken tests7 which leads to frowns. ontinuin. from before: function test*d2in#dit() { $this->Posts->"ession->;rite('*uth1!ser', arra8( 'id' => 1, 'userna2e' => '2arJstor8', )); $this->Posts->data = arra8( 'Post' => arra8( 'id' => 9, 'tit6e' => 'Kest artic6e #varE', '7od8' => 'so2e text', ), '&ag' => arra8( '&ag' => arra8(1,9,M), ) ); $this->Posts->7efore,i6ter(); $this->Posts->%o2ponent->startup($this->Posts); $this->Posts->ad2in3edit(); HHassert the record ;as changed $resu6t = $this->Posts->Post->read(nu66, 9); $this->assert#:ua6($resu6t<'Post'=<'tit6e'=, 'Kest artic6e #varE'); $this->assert#:ua6($resu6t<'Post'=<'7od8'=, 'so2e text'); $this->assert#:ua6("et@@extract('H&agHid', $resu6t), arra8(1,9,M)); HHassert that so2e sort of session f6ash ;as set1 $this->assert&rue($this->Posts-> "ession->checJ('.essage1f6ash12essage')); $this->assert#:ua6($this->Posts->redirect!r6, arra8('action' => 'index'));

*1

$o there you .o7 a nice simple test for a controller7 with redirects and session flashes. $ince we are testin. with the real session7 we should do the followin. to ensure there is no bleed-throu.h between tests: function end&est() { $this->Posts->"ession->destro8(); unset($this->Posts); %6ass-egistr8@@f6ush();

By destroyin. the session we ensure that we have a clean slate on each test method. $o that4s it7 really. Testin. controllers really isn4t as hard as it may seem. There are some additional tricks that can be done with Mocks but that is another article all to.ether.

*1

.o%k -"<e%ts
This section contributed by Mark $tory. Bri.inally posted at http://markstory.com/posts/view/testin.-cakephp-controllers-mock-ob;ects-edition 5 recently wrote an article about testin. ake'(' controllers the hard way where 5 covered testin. controllers by runnin. their methods manually. 5 hinted at some additional tricks that could be performed by usin. Mock Bb;ects. Today 54m .oin. to spill the beans on Mocks7 and how 5 use them when testin. my ontrollers. What is a .o%k -"<e%t #irst7 fi.urin. out what Mock ob;ects are and are not is important. The "ikipedia says: 5n ob;ect-oriented pro.rammin.7 mock ob;ects are simulated ob;ects that mimic the behavior of real ob;ects in controlled ways. & computer pro.rammer typically creates a mock ob;ect to test the behavior of some other ob;ect7 in much the same way that a car desi.ner uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts. 5n unit testin. we use mocks both as a way to isolate our unit /the ob;ect bein. tested0 from the world and to allow us to create une9pected situations. Gver wonder how your application would react if a random value was in;ected7 or wonder how you can easily tri..er that e9ception that relies on #ooBar omponent returnin. false which it only does if the file system is corruptedE "ell7 these are both situations that can be simulated with Mock Bb;ects. Mock ob;ects can also take an active role in ,nit testin. and contribute additional assertions to your tests in the form of e9pectations. Mocks are not the solution to all your testin. woes7 nor will they make a .ood cup of coffee7 but movin. on. Where %an 1 3et one o& these &a"ulous de!i%es8 "ell7 since we are workin. in the conte9t of a ake'(' unit test and therefore usin. $imple Test7 we .et Mock ob;ects from the Mock class. "e .enerate Mock ob;ects from e9istin. classes7 and due to the ma.ic of Ceflection and eva6()7 a Mock ob;ect class is .enerated. The simplest e9ample would be: *pp@@i2port('%o2ponent', '#2ai6'); .ocJ@@generate('#2ai6%o2ponent'); HH&hen ;hen ;e need it $this->Posts->#2ai6 = ne; .ocJ#2ai6%o2ponent(); This will .enerate a class called MockGmail omponent. This MockGmail omponent class will have all the same methods as our real Gmail omponent. Bne bi. difference between the real ob;ect and the mock is that all of the mock methods do not work. They all return null and take any number of ar.uments. But hold on a second7 if its methods don4t have returns what .ood are theyE "ell7 lots7 because you can set the return value or return reference value of all its methods. $this->Posts->#2ai6->set-eturnIa6ue('send', true); **

This will set the return value of send/0 to true. (owever7 it will not send an email7 which is the nice part. Because .ettin. emails from your test suite is never fun. &lso it allows the test to run on machines that don4t even have e-mail servers7 like a development bo9. ,sin. a Mock to test email bein. sent also allows you to test what happens when your email server is down or other difficult to simulate situtations. >eneratin. a Mock ob;ect also allows you to append e9tra methods onto your ob;ects7 such as those you are .oin. to build but haven4t. This would look a little somethin. like: .ocJ@@generate('#2ai6%o2ponent', '.ocJ#2ai6%o2ponent', arra8('s6ice', 'dice')); "e4ve now added the methods slice/0 and dice/0 to our MockGmail omponent. 5n addition to complete Mocks7 we can build partial Mocks. & partial Mock is ;ust what it sounds like. 5t has only a few of its methods mocked. The rest stay as is in the declared class/es0. This is really handy for ob;ects that use only a few methods to write to a resource. &n e9ample of this would be your controllers. 5n my previous article 5 used a subclass to dummy out the render and redirect methods. (owever7 we could also do this with partial Mock ob;ects. .ocJ@@generatePartia6('Posts%ontro66er', '&estPost%ontro66er', arra8('render', 'redirect')); 5n our tests we can now use Mock e9pectations to assert that our redirects and render calls are occurrin. properly. .akin3s e;2e%tations with .o%k -"<e%ts Mocks can be used to feed your application values7 as seen above. #urthermore7 Mock Bb;ects can be used to introspect on your unit and ensure that it is properly dele.atin. / callin. methods on its inner ob;ects. $ay for e9ample we wanted to test the use of $ession omponent::set#lash/0. )ow7 we could not mock it7 and make an assertion before and after the method has run to test that the value in the session was not set and then is set. This will work ;ust fine until we start addin. lots of test methods that use the session. "e could easily run into bleed-throu.h between our tests7 causin. our tests to become dependent on the order in which they are run7 or worse yet7 create broken tests. This is no .ood. #urthermore7 usin. the real session will nuke any sessions we have with the bo9 we are testin. on. ,sin. a Mock ob;ect for our session solves all of these problems. HH02port and generate the 2ocJ ;e ;ant1 *pp@@i2port('%o2ponent', '"ession'); .ocJ@@generate('"ession%o2ponent'); HH0n one of our test 2ethods $this->Posts->"ession = ne; .ocJ"ession%o2ponent(); $this->Posts->"ession->set-eturnIa6ue('read', 1, arra8('*uth1!ser1id')); $this->Posts->"ession->expect'nce('set,6ash'); $this->Posts->ad2in3edit(); *2

5n the above we4ve not only set a return value for our $ession omponent4s read/0 method when passed the ar.ument of &uth.,ser.id7 but we4ve also made an e9pectation that set#lash/0 will be called e9actly once. 5f it is called twice or never this assertion will fail7 and we will .et the red bar of doom. )otice that 5 didn4t make an assertion on the value bein. passed to set#lash/0. 8ou totally can e9pect certain parameters to be passed to mock methods. (owever7 5 find settin. assertions for the values bein. fed into methods like set#lash/0 can be sub;ect to a lot of chan.e. 5f we were to make assertions on these inputs7 we would need to update our tests each time the messa.e chan.es. 5 personally find 54m more interested that the method is called7 .ivin. the user feedback7 than what the e9act contents of that feedback are. There is so much that can be said about mock ob;ects. (owever7 this post is lon. enou.h for the time bein.. Be sure to check out the $impleTest =ocumentation on Mock Bb;ects for a complete reference on the &'5 and more additional information about $impleTest mock ob;ects.

*3

.odels
Test Case &s of ake'(' 1.1 #inal7 the bake console will produce model test cases usin. the lassCe.istry to .et an instance of the test model. This saves you from havin. to e9tend the model you want to test and e9plicitly set it to use the test database. Make sure your test model is created like this: function start&est() { $this->Post =B %6ass-egistr8@@init('Post');

5f you are still usin. &pp::import and then creatin. an instance of your model7 it:s time to up.radeR The downside to usin. the lassCe.istry method is that it will automatically load all the related models. $o you have to include fi9tures for all the related models as well7 even if you aren4t .oin. to be testin. them. )i;tures 5f you open up the default fi9ture created by the console you4ll probably notice a lar.e block of code similar to this: var $fie6ds = arra8( 'id' => arra8('t8pe'=>'integer', 'nu66' => fa6se, 'defau6t' => (!$$, 'Je8' => 'pri2ar8'), 'tit6e' => arra8('t8pe'=>'string', 'nu66' => fa6se, 'defau6t' => (!$$), 'content' => arra8('t8pe'=>'string', 'nu66' => fa6se, 'defau6t' => (!$$), 'created' => arra8('t8pe'=>'dateti2e', 'nu66' => true, 'defau6t' => (!$$), '2odified' => arra8('t8pe'=>'dateti2e', 'nu66' => true, 'defau6t' => (!$$), ); #or most test fi9tures7 it really isn4t necessary to re-define the table4s scheme in your fi9ture file. 5f you make chan.es in the schema you4ll need to update the fi9ture as well. 8ou can simply remove this block and replace it with a line tellin. the fi9ture to use the same schema as the actual table. var $i2port = arra8('ta76e' => 'posts', 'i2port' => fa6se); Make sure the test database is completely empty - no tables at all - when you run your tests. 5f your test case fails because of a '(' error7 it may leave a table han.in. around. This will mess up your test the ne9t time you try to run it7 which will lead to a cycle of tests that are failin. for no reason. 8ou4ll waste hours and .o borderline insane. Make sure your =B is emptyR

*<

.er3in3
The Controller

dd and +dit %tions

Bften times when creatin. an application you4ll have the need for both add and edit pa.es. The default controller and views created when usin. the bake console treat these as separate areas7 which leads to a fair amount of duplicate code. These are the default add and edit actions created by bake console: function add() { if (Ee2pt8($this->data)) { $this->Post->create(); if ($this->Post->save($this->data)) { $this->f6ash(33('!ser saved1', true), arra8('action'=>'index')); e6se {

function edit($id = nu66) { if (E$id BB e2pt8($this->data)) { $this->f6ash(33('0nva6id Post', true), arra8('action'=>'index')); if (Ee2pt8($this->data)) { if ($this->Post->save($this->data)) { $this->f6ash(33('&he Post has 7een saved1', true), arra8('action'=>'index')); e6se { if (e2pt8($this->data)) { $this->data = $this->Post->read(nu66, $id);

8ou can see the entire add method is duplicated in the edit method. "e can basically .et rid of the add method alto.ether and let the edit method handle the creation of new items. There are a couple of different ways to handle this. 8ou can remove add/0 entirely by addin. a route that points /add to the edit action. The second route is for apps with prefi9 routin. enabled. -outer@@connect('H@contro66erHadd', arra8('action' => 'edit')); -outer@@connect('H@prefixH@contro66erHadd', arra8('action' => 'edit')); *D

)ow some chan.es to the edit action need to be made so that it can handle adds as well. Cemove the first if block: if (E$id BB e2pt8($this->data)) { $this->f6ash(33('0nva6id Post', true), arra8('action'=>'index'));

han.e the last if statement from: if (e2pt8($this->data)) { to: if ($id BB e2pt8($this->data)) { 8our edit action should now look like this: function edit($id = nu66) { if (Ee2pt8($this->data)) { if ($this->Post->save($this->data)) { $this->f6ash(33('&he Post has 7een saved1', true), arra8('action'=>'index')); e6se { if ($id BB e2pt8($this->data)) { $this->data = $this->Post->read(nu66, $id);

Tellin3 dd nd +dit 2art


There are times when it may be useful to distin.uish which action is really bein. called. Maybe it4s for & - reasons7 where you want to allow a user to edit a record7 but not create a new one. By settin. the route as described above your controller4s $this->action will always be HeditI. 8ou could add an additional parameter to the route7 which would be used to detect the intended action: -outer@@connect('H@contro66erHadd', arra8('action' => 'edit', 'orig*ction' => 'add')); )ow it is simply a matter of checkin. if the ori.&ction is set and usin. that instead: $action = Ee2pt8($this->para2s<'orig*ction'=) D $this>para2s<'orig*ction'= @ $this->action; *K

The :iew
8ou can delete the add.ctp view file7 since it is no lon.er called anymore. The add.ctp and edit.ctp files are e9tremely similar by default7 so doin. this will save you a lot of code redundancy. The edit view will work for adds without any modifications. 8ou may want to make a chan.e the le.end to read differently dependin. on the action. $action = Ee2pt8($this->para2s<'orig*ction'=) D $this>para2s<'orig*ction'= @ $this->action; echo sprintf(33('?s ?s', true), 33(uc;ords($action), true), 33('Post', true)); This will chan.e the le.end to read either 6&dd 'ost6 or 6Gdit 'ost67 dependin. on the action.

*L

Cake Tri%ks &ro# The Core


Cake Style $o2tions Para#eter
"e4ve all seen functions that start out takin. one parameter and then7 as they .row7 they end up lookin. like this: function geo$ocationQ.$($prog0d=nu66, $user0d=nu66, $start>ate=nu66, $end>ate=nu66, $a66Progs=fa6se, $detai6=true) { if(E$prod0d) { $prod0d = $this->id; if(E$user0d) { $user0d = !ser@@get('id'); if(E$start>ate) { $start>ate = strtoti2e('-1 2onth'); if(E$end>ate) { $end>ate = ti2e();

Many ake functions solve this problem by takin. an Joptions parameter7 which is a keyed array. This allows the option list to .row without throwin. the function declaration out of whack. "ithin the function7 defaults can be set for the various parameters. (ere4s the function above rewritten with Joptions support: function geo$ocationQ.$($options = arra8()) { $options = arra832erge(arra8('prod0d' => $this->id, 'user0d' => !ser@@get('id'), 'start>ate' => strtoti2e('-1 2onth'), 'end>ate' => ti2e(), 'a66Progs' => fa6se, 'detai6' => true), $options);

This approach mer.es the passed Joptions with a default options array. The end Joptions contains the values passed7 plus the defaults for any key that wasn4t included. The ake Joptions approach is easier to read7 shorter to code and much more e9tensible.

2+

Handlin3 9ata rrays with a Sin3le Re%ord or an rray o& Re%ords


There are times when it is useful to have a function that can handle a sin.le data record or an array of records. ,nfortunately7 since Jdata is an array in both cases7 it can be difficult to tell which type it is. The trick is with the type of array. 5f it is ;ust a sin.le record7 it is a keyed array somethin. like: *rra8 ( <Post= => *rra8 ( <id= => 1 <tit6e= => Post 1 <7od8= => $ore2 ipsu2 do6or sit a2et111 <created= => 900L-01-9M 1N@OM@P9 <updated= => 900L-09-0M 19@0R@PS ) ) 5n the case where there are multiple records7 you will have an inde9ed array: *rra8 ( <0= => *rra8 ( <Post= => *rra8 ( <id= => 1 <tit6e= => Post 1 <7od8= => $ore2 ipsu2 do6or sit a2et111 <created= => 900L-01-9M 1N@OM@P9 <updated= => 900L-09-0M 19@0R@PS ) ) <1= => *rra8 ( <Post= => *rra8 ( <id= => 9 <tit6e= => Post &;o <7od8= => $ore2 ipsu2 do6or sit a2et111 <created= => 900L-01-9P 1O@OM@OM <updated= => 900L-09-0M 1N@1M@90 ) )

21

The trick to tellin. them apart is to look at the keys. 5f they are all numeric7 it is safe to assume you are dealin. with an array of data records. ake provides a very simply way to do this - the "et@@nu2eric method. "et@@nu2eric(arra83Je8s($data)); Then it is simply a matter of convertin. that sin.le record into an array of records /althou.h there is still only one entry0. if(E"et@@nu2eric(arra83Je8s($data)) { $data = arra8($data);

8our function can now handle a sin.le record or an array of records7 re.ardless of how that data is passed. 8ou ;ust loop throu.h $data and process each of the records. foreach($data as $i => $record) { HHdo stuff here

21

Stu2id +asy 0RL Slu3s


54ve seen this idea floated on a bunch of different blo.s. Many offer more complicated methods or behaviors/helpers to simplify thin.s. This is the most basic7 stupidly easy way to accomplish it. 5f you are viewin. a sin.le item within a ake site7 your ,C- will be somethin. like HpostsHvie;H O7 where HpostsI is the model and H3I is the id of the item. To .enerate a link to HpostsHvie;HO you4d use code like this: $ht26->6inJ('%aJeP5P &ips', arra8('contro66er' => 'Post', 'action' => 'vie;', O)); But you probably aren4t passin. in the id directly7 passin. the element of a data array instead. $omethin. like: $ht26->6inJ($post<'Post'=<'tit6e'=, arra8('contro66er' => 'Post', 'action' => 'vie;', $post<'Post'=<'id'=)); &ssumin. you4re usin. a standard action for your view7 like: function vie;($id=nu66) { 111 5t is safe to create ,C-s with e9tra parameters. #or e9ample7 the ,CHpostsHvie;HOHcaJephp-tips will render the e9act same as HpostsHvie;HO. That means you can sneak the slu. onto your link and not have to make any chan.es in your controller. ake'(' even provides a method for creatin. slu.s: 0nf6ector@@s6ug. 8ou can now create your links like this: $ht26->6inJ($post<'Post'=<'tit6e'=, arra8 ( 'contro66er' => 'Post', 'action' => 'vie;', $post<'Post'=<'id'=, 0nf6ector@@s6ug($post<'Post'=<'tit6e'=) ) ); This will .enerate the ,C- HpostsHvie;HOHcaJephp3tips. "ith this method you don4t have to worry about keepin. track of slu.s in your database or re-routin. slu.s if the title chan.es.

2*

.akin3 The Slu3s

Bit S#arter
Hy2hen

Chan3in3 The 0nders%ore To

5f you prefer 4-4 instead of 4%4 as the replacement for spaces7 ;ust pass it as the second parameter to 0nf6ector@@s6ug like this: 0nf6ector@@s6ug($post<'Post'=<'tit6e'=, '-') Consisten%y nd S+- 1#2ro!e#ents ,sin. this method for your slu.s means that anythin. is allowed in the ,C- and your site will still display it. #or instance7 someone could make the ,C- be HpostsHvie;HOHcaJephp3sucJs3and3so3does38our3site and your site would happily display the content inde9ed at id 3. )ot only does this allow for malicious links7 but it can hurt your $GB by havin. so many different links point to the same content. 5t4s easy enou.h to check if the slu. bein. reFuested matches the intended slu. and redirect the user if it doesn4t. 5n your controller4s action7 after you load the post7 put: 0f(0nf6ector@@s6ug($post<'Post'=<'tit6e'=) E= $this->para2s<'pass'=<1= TT count($this->para2s<'pass'=) E= 9) { $post = $this->Post->read(nu66, $id); $this->redirect(arra8($id, 0nf6ector@@s6ug($post<'Post'=<'tit6e'=), M01));

22

<=uery
The Aava$cript and &A&P helpers for ake'(' both make use of the 'rototype and script.aculo.us libraries. &lthou.h these are both solid libraries7 many developers are shiftin. to ;Muery for their Aava$cript needs. $ome may view not havin. a helper for ;Muery as a ne.ative7 but 5 probably wouldn4t use it anyway. 5 prefer to write my ;Muery code directly.

Re2la%in3 $<a!as%ri2t6>e!ent?@
The Aavascript(elper used the 'rototype library for the event method7 which is tri..ered based on user interaction with the pa.e. 5f you were usin. the ake'(' helper7 you4d write somethin. like: CDphp echo $Uavascript->event('do20d', 'c6icJ', 'function() { a6ert(Fc6icJedF)'); D> This would fire an alert with the messa.e 6clicked6 anytime the =BM element with the id 6dom5d6 was clicked. )otice that this is '(' code that will .enerate the proper Aava$cript. To accomplish the same thin. with ;Muery you would use: $(FVdo20dF)1c6icJ(function() { a6ert(Fc6icJedF) );

This is pure Aava$cript code and can be placed directly in the view or in an e9ternal A$ file. #or the full list of ;Muery events see http://docs.;Fuery.com/Gvent. &lso7 ;Muery has very powerful selector support7 so you4re not ;ust stuck usin. a =BM 5= - check out http://docs.;Fuery.com/$electors.

Re2la%in3 $a<a;6>link?@
The &;a9(elper4s link method creates a special link that7 when clicked7 sends off an PM-(ttpCeFuest7 rather then redirectin. the browser. The html that is returned can then be placed in an element in the pa.e. The code for this7 usin. the &;a9(elper7 would be: CDphp echo $aUax->6inJ('!pdateE', 'HscoresHupdate', arra8('update' => 'div0d')); D> This will create a link with the word 6,pdateR4. "hen it is clicked the &;a9 reFuest will be made to the $cores ontroller7 update action. The resultin. html will be displayed in the =BM element with the id 6div5d6.

23

To accomplish the same thin. with ;Muery7 you would first create the html link as normal7 .ivin. it an 5=: CDphp echo $ht26->6inJ('!pdateE', 'HscoresHupdate', arra8('id' => 'scores!pdate')) D> Then you would catch the click event for this link and make the &;a9 reFuest. $(FVscores!pdateF)1c6icJ(function() { $1aUax({ ur6@ FHscoresHupdateF, success@ function(ht26) { $(FVscores!pdateF)1ht26(ht26); ); ); This is ;ust the tip of the iceber. with ;Muery. heck out http://docs.;Fuery.com/Tutorials:(ow%;Muery%"orks for more.

2<

+;2andin3 Trees With <=uery


(ere4s a dead simple way to make an e9pandin. tree system usin. ;Muery. To pull this off we4re .oin. to use the Tree(elper by &ndy =awson /&=Dsi90 and the Treeview ;Muery plu.in by ASrn Taefferer.

Basi% Tree
#irst take a look at the e9ample tree behavior code in the runnin.. 8ou should end up with somethin. like this: ookbook. >o ahead and .et that

TreeHel2er
)e9t7 put a copy of the Tree(elper from bakery.cakephp.or. into your /app/helpers directory and include it in your controller. 8ou will also need to chan.e the Fuery to retrieve your data. 8ou can use the built in 4threaded4 find type to .et the tree data in a format suitable for the Tree 2D

(elper. (ere4s the full controller code: CDphp c6ass %ategories%ontro66er extends *pp%ontro66er { var $na2e = '%ategories'; var $he6pers = arra8('&ree'); function index() { $categories = $this->%ategor8->find('threaded'); $this->set('categories', $categories); D> reate a Hvie;sHcategoriesHindex1ctp file with: CDphp echo $tree->generate($categories); D>

2K

Cefresh /cate.ories in your browser and you should end up with:

2L

The (TM- that is outputted is a standard unordered list. Cu6> C6i>.8 %ategories Cu6> C6i>,un Cu6> C6i>"port Cu6> C6i>"urfingCH6i> C6i>#xtre2e JnittingCH6i> CHu6> CH6i> C6i>,riends Cu6> C6i>Gera6dCH6i> C6i>G;endo68nCH6i> CHu6> CH6i> CHu6> CH6i> C6i>4orJ Cu6> C6i>-eports Cu6> C6i>*nnua6CH6i> C6i>"tatusCH6i> CHu6> CH6i> C6i>&rips Cu6> C6i>(ationa6CH6i> C6i>0nternationa6CH6i> CHu6> CH6i> CHu6> CH6i> CHu6> CH6i> CHu6>

TreeHel2er With <=uery


=ownload the Treeview plu.in and e9tract the ?ip. To .et the Treeview plu.in set up you need to copy three items into your webroot. 'ut ;Fuery.treeview.;s /or one of the minified versions0 in your /app/webroot/;s. 'ut ;Fuery.treeview.css in your /app/webroot/css. 'ut the ima.es folder in /app/webroot/css. "e4ll move this to a better spot later7 but for the sake of .ettin. this workin. we4ll leave it there for now. 3+

Bbviously7 you4ll also need the ;Muery library. 5nclude the two Aava$cript files and the your view or layout. $Uavascript->6inJ(arra8('U:uer8', 'U:uer81treevie;'), fa6se); $ht26->css('U:uer81treevie;', nu66, nu66, fa6se);

$$ file in

&lso7 make sure you have included the Aavascript(elper class in your controller or &pp ontroller class. var $he6pers = arra8('/avascript', '&ree');

The Treeview plu.in needs a way to tar.et your tree list. The easiest way to do this to add an element id to the first Cu6>. echo $tree->generate($categories, arra8('id' => 'tree')); )ow all you have to do is point the Treeview plu.in at your unordered list and let it do its thin.. Cscript t8pe=FtextHUavascriptF> $(docu2ent)1read8(function(){ $(FVtreeF)1treevie;(); ); CHscript>

31

Cefresh your browser and you should now be lookin. at a collapsible tree with all the nodes e9panded.

The Treeview plu.in has a bunch of different option for how to display or animate the tree. $ee the Treeview documentation for the full details.

Cleanin3 02 the 1#a3es


-eavin. the Treeview ima.es in /app/webroot/css/ima.es isn4t really a .reat idea. $o instead7 let4s move them to /app/webroot/im./treeview. The ima.es are all referenced in the Treeview $$ file7 so you4ll need to open that and fi9 the paths. =o a find and replace on 6ima.es/6. 8ou can replace it with the absolute path /6/im./treeview/60 or the relative path /6../im./treeview/60. $ave the $$ file and refresh the cate.ories pa.e in your browser. Gverythin. should look the same as before. 31

Aa!aS%ri2t 1n :iews
5n some corners of the development world people are morally a.ainst puttin. Aava$cript HinlineI with the (TM-. &nytime you put Aava$cript in a ake view it will be rendered in the middle of the content. Bne easy way to .et around this is to use the $Uavascript->codeK6ocJ() function with the HinlineI option set to false and the (eredoc synta9. 5n the previous section code that would appear in a view7 like this: Cscript t8pe=FtextHUavascriptF> $(docu2ent)1read8(function(){ $(FVtreeF)1treevie;(); ); CHscript> ould alternately by written: CDphp $Uavascript->codeK6ocJ( CCC#(> $(docu2ent)1read8(function(){ $(FVtreeF)1treevie;(); ); #(> , arra8('in6ine' => fa6se)); D> This code would .et written to your C5#*>> section7 in place of the $scripts3for36a8out variable. The resultin. output would be: Cscript t8pe=FtextHUavascriptF> HHCE<%>*&*< $(docu2ent)1read8(function(){ $(FVtreeF)1treevie;(); ); HH==> CHscript> & couple notes:
: :

The #(> identifier must appear on a line by itself /or with a sin.le closin. ;0 and no indentation or trailin. spaces. (eredoc parses '(' variables7 much like double Fuotes. $o you can use any view variables in the strin.. (owever if you wanted to the J to appear in the Aava$cript it would need to be escaped with a backslash/U0. The reason this isn4t needed in the above e9ample is that $(111) isn4t a valid '(' variable and therefor isn4t interpreted. 3*

.ake /our Cake

22 )ast

9onBt 0se $uses 0nless /ou ReallyC "solutely Ha!e To


$uses is a controller attribute that allows you to access additional models to the default one. $ay you have a blo. application and one of the controllers is posts. By default you have access to the 'ost model. 5f you wanted to also have access to the omment model you could do: CDphp c6ass Posts%ontro66er extends *pp%ontro66er { var $na2e = 'Posts'; var $uses = arra8('Post', '%o22ent'); D> .odel Chains $ince omment is associated to 'ost throu.h a (asMany relationship you can access the omment model throu.h 'ost. -ike this: $co22ents = $this->Post->%o22ent->find*66K8Post0d($id); The relation chain e9tends infinitely7 includin. all models down the line. Controller44load.odel and ClassRe3istry44init. >reat7 but sometimes you do le.itimately need access to a model that isn4t anywhere in the relation chain. 5f you are .oin. to use the model throu.hout the controller .o ahead and include it in $uses7 but if you only need it in one action there are better ways. They are %ontro66er@@6oad.ode6() and %6ass-egistr8@@init(). HHthe 6oad.ode6 ;a8 $this->6oad.ode6('%o22ent'); $co22ents = $this->%o22ent->find*66K8Post0d($id); HHthe %6ass-egistr8 ;a8 $%o22ent = %6ass-egistr8@@init('%o22ent'); $co22ents = $%o22ent->find*66K8Post0d($id); %ontro66er@@6oad.ode67 not to be mistaken for the deprecated 6oad.ode6 function7 creates an instance of the model and assi.ns it to the controller. 8ou can then access it the same way as you would as if it was loaded throu.h $uses. lassCe.istry returns an instance of the model. 22ro;i#ate 1n%rease4 (avin. one or two e9tra models in your controller4s $uses probably isn4t .oin. to kill your app. "ith one e9tra model there was about a 2N increase. $even e9tra models added appro9imately 2+N increase in pa.e load. Cou.hly 2-<N for every additional model is a .ood rule of thumb. 32

0se Containa"le
5 described this one above. >o read that section7 then come back.

Set 9e"u3 to D
This one should be a no-brainer7 but enou.h people miss it7 so 54m .oin. to include it. #or the ake en.ine to run it .enerates two cache sections. The first is Ht2pHcacheH2ode6s. 5n there you:ll find a file for every model in your system containin. the table schema. 8ou know those >#"%-0K# ta76e; Fueries you see in the Fuery outputE That:s what they4re for. Those Fueries .o away when debu. is +. The second cache is Ht2pHcacheHpersistent. There are a couple different files in there that are used by ake when runnin. your app. The one that .enerally causes the most slow down to .enerate is caJe3core3fi6e32ap. This file stores the paths to various classes in your app. To build the file ake does a lo.ical7 but still time consumin.7 search of your directory tree lookin. for the ri.ht file. $o what is the difference between debu. + and debu. V+E Bh7 about 1.D*31D1+2 years. "hen debu. is V+ the cache lifetime on these files is 1+ seconds. $witchin. debu. to + pushes the e9piration to LLL days. 22ro;i#ate 1n%rease4 WK+N to 1++N

Ca%he your slow Eueries,we" ser!i%e reEuests,whate!er


The ake cache lib is a .reat tool for cachin. sin.le parts of your application. 5t handles all the .ory work of writin. to a file or tyin. into a memory-based cachin. en.ine. &ll you need to do is fi.ure out what to cache. -et:s say you have a Fuery that has been inde9ed and optimi?ed7 but is still too slow. The ookbook provides an e9ample of how to wrap it with the cache lib so that you don:t need to run it every reFuest. Br if you have a part of your site that is filled with data returned from a web service7 like a recent tweets block /not a .reat e9ample7 since most of the Twitter wid.ets are Aava$cript7 but roll with me here0. There really is no reason to make the call to the web service on every reFuest. Aust wrap it with the cache lib like in the above e9ample. 22ro;i#ate 1n%rease4 W+N to 1++++++N7 it really depends on your app and what you are cachin..

:iew Ca%hin3
Think of this as entire pa.e cachin.. The ookbook covers the basics and since renderin. the pa.e still runs throu.h '('7 there is some fle9ibility for maintainin. dynamic parts of the pa.e. #or e9ample7 if you were runnin. a store you could cache the product pa.es7 but still have a block showin. the user:s shoppin. cart. 33

Fote4 There:s a section in the ookbook mi9ed in here that covers the various cachin. en.ines ake'(' supports. (owever7 at the moment /version 1.1.10 view cachin. uses file based cachin. and is independent of the cache library. 22ro;i#ate 1n%rease4 W1*+N to 1<+N

HT.L Ca%hin3
This one is my own creation. 5t:s based on the same principal of the $uper ache for "ord'ress. Basically7 it takes the rendered pa.e and writes it to your webroot as strai.ht (TM-. The ne9t time the pa.e is hit7 your web server can serve it directly without even havin. to .o to '('. There are obvious limitations for this7 such as no dynamic content on the pa.e and the cache won:t be automatically cleared. $till7 it:s .reat for thin.s like C$$ feeds7 &'5s7 documentation7 etc. &nywhere the anonymous viewers all .et the same pa.e. 22ro;i#ate 1n%rease4 X<++++N - This isn:t hyperbole7 that:s the real increase.

PC ?or so#e other o2%ode %a%he@


"ikipedia describes &' as Ha free7 open source framework that optimi?es '(' intermediate code and caches data and compiled code from the '(' bytecode compiler in shared memory.I "hatever. 5t makes shit fast. &nd you don:t have to chan.e any of your code. #uck yea. "here do 5 si.n up7 ri.htE 22ro;i#ate 1n%rease4 W13N to 1++N

Persistent .odels
This one isn:t mentioned in the ookbook /5:ll add it in the ne9t few days if no one beats me to it. 5 put it on my todo whiteboard7 ri.ht below Hfi.ure out why puttin. computers in the clouds is more efficient than their traditional .round based counter partsI0. This one is simple to turn on. 5n your controller /or &pp ontroller0 add the attribute: var $persist.ode6 = true; &fter a pa.e refresh7 you:ll notice two new files in Ht2pHcacheHpersistent for each model included in the controller. Bne is a cache of the model and the other is a cache of the ob;ects in the lassCe.istry. -ike view cachin. mentioned above7 this cache can only be saved on the file system. 22ro;i#ate 1n%rease4 W+N to 1++N (ow much this one helps depends on your application. 5f your controller only has one model and it isn:t associated with any others7 you:re not .oin. to see much of a boost. 5n my demo app there was around 1++N increase. There was one model in the controller7 which was associated with * 3<

other models7 which had associations of their own. Warnin34 This one seems to cause issues for a lot of people. 5t tends to work best in simpler apps where there is a one-to-one model-to-controller ratio. Bnce you start loadin. models in different controllers7 the ob;ects cache associated with the model may not match and you4ll start .ettin. errors.

Store The Persistent Ca%he in PC


To enable this you need be usin. &' core.php put: and set your H%cake%core%I cache to use &' . 5n your

%ache@@config('3caJe3core3', arra8('engine' => '*pc', 'duration'=> MN00, 'pro7a7i6it8'=> 100, ));

This takes the cache files normally stored in Ht2pHcacheHpersistent /not includin. the persistent models0 and stores them in memory. 22ro;i#ate 1n%rease4 X13N

S2eed 02 Re!erse Routin3


There are two methods for doin. this. The first is described in a post by Tim at =ebu..able.com. Tim:s method only works for certain link types and breaks the reverse routin. feature. 5 created a different approach that uses cachin.. There is a post describin. it at 'seudo oder.com and the code is available at >it(ub. 22ro;i#ate 1n%rease4 X3+N -ike all of these tips7 the actual increase depends on your app. 5f you don:t use many custom routes and don:t have many links on your pa.e7 you4re not .oin. to see much of a benefit.

0n%hain /our .odels


Make sure you understand model chainin. before readin. this section. 5t is described above in the H=on4t ,se uses $ectionI and a little bit in the Model section of the ookbook. & lot of you probably reali?ed that althou.h it4s .reat to be able to access related models throu.h chainin.7 buildin. the chain on every reFuest is not very efficient. ,sin. persistent models attempts to solve that problem by cachin. the ob;ects7 but this doesn4t work in every application. 'articularly7 if you access models usin. %ontro66er@@6oad.ode6 or %6ass-egistr8@@init7 usin. persitent models will often .ive errors such as:

3D

Catchable fatal error: Object of class __PHP_Incomplete_Class Cather than cachin. the models7 it makes sense to not create the chain by default and instead add the links as needed. Thanks to '('4s 33get() and 33isset() this is possible. These functions are part of '('4s overloadin. ability. To accomplish this check out my -a?y-oader plu.in available from >it(ub. ,nfortunatly7 33isset() is only available in '(' 3.1.+ and above7 so this code is limited to those versions. 22ro;i#ate 1n%rease4 Cou.hly 2-<N for every model that is unchained. 8ou4ll see the bi..est .ain in applications with many interconnected models.

3K

The 7iant Con&i3urationC :ersion Control and 9e2loy#ent Se%tion


The topic of handlin. confi.uration7 multiple environments7 and deployment has been covered e9tensively. &t the end of this section are some links to alternate methods. The way described here is much simpler7 reFuires virtually no codin.7 and isn:t dependent on any server detection.

:ersion Control
There are three main confi.uration files for ake'('. #or each one you:ll need to decide whether the actual file or ;ust a template of the file is kept under version control . %ore.2h2 @eep in version control: /es The core.php confi.uration file contains a set of .lobal confi.uration options for your application. 5n .eneral these options apply to the application7 re.ardless of the environment. 5t is true that you may need to chan.e a specific settin. dependin. on the app instance. The Hdebu.I settin. is the most common. This will be covered in the bootstrap.php section below. "ootstra2.2h2 @eep in version control: /es and Fo 5 find it convenient to split the bootstrap file into two files. The normal 7ootstrap1php and a second 7ootstrap16oca61php. The ori.inal 7ootstrap1php is maintained in source control7 while the local version is not. 8ou may choose to keep a 7ootstrap16oca61php1te2p6ate in your source control7 which shows some sample settin.s. The local version of the bootstrap is included at the bottom of the main bootstrap file like this: CDphp HH so2e 7ootstrap code here if(fi6e3exists(W7ootstrap16oca61phpX)) { inc6ude(W7ootstrap16oca61phpX); HH#', D> This method allows you to maintain your application wide defaults in core.php and bootstrap.php7 then override them for any particular environment. #or e9ample7 on your dev machine you:ll likely want to turn on debu.. 5nstead of editin. core.php7 add the line to your bootstrap.local.php. %onfigure@@;rite('de7ug', 9);

3L

data"ase.2h2 @eep in version control: Fo To me7 this one is the easiest. =on:t put it in version controlQ instead maintain a template of sample settin.s. 5 know a lot of people don:t a.ree with me here. 8ou would never put this in version control with a sin.le $defau6t database7 since the settin.s for that database would most definitely need to be chan.ed for each development environment. Then there would be the dan.er of these development settin.s .ettin. checked in by mistake. This may .et cau.ht before the file is actually deployed to production7 but you would still have to deal with the constant annoyance of your local data7ase1php .ettin. overwritten. & second option is to have multiple variables for each environments database. $ay $deve6op2ent and $production7 like this: c6ass >*&*K*"#3%'(,0G { var $deve6op2ent = arra8( 'driver' => '28s:6', 'host' => '6oca6host', '6ogin' => 'root', 'pass;ord' => '', 'data7ase' => 'dev3app' ); var $production = arra8( 'driver' => '28s:6', 'host' => 'd7128app1co2', '6ogin' => 'production', 'pass;ord' => 'UuMuspaKu7re;rap', 'data7ase' => 'prod3app' );

Then you:d have some lo.ic somewhere that detects the current server and selects the appropriate database confi.. This would likely lead to the same problems as with the sin.le Jdefault option7 as the $deve6op2ent array may be different for each developer and would lead to headaches if the file was committed. 5n addition7 your production database settin.s are now available to anyone with source control access. Many times you won:t want all the developers to have this kind of access7 whether they are a freelancer or full-time. >ranted7 this info may not be enou.h to access the database alone7 but why risk itE &lso7 any accidental chan.es made to the $production array are unlikely to be cau.ht until the file is actually deployed to production. #inally7 is it really that hard to copy the template to database.php and fi9 the settin.sE (ow often do you even do itE Bnce7 twiceE 5nstead you:re .oin. to rely on the $3"#-I#- variableE &nd what happens when you want to roll out a new environmentE 8ou need to update and commit the code so that it knows about this new instance. ,.h. "hy add all the e9tra troubleE

<+

.ulti2le +n!iron#ents
Based on the plan outlined above7 core.php and bootstrap.php are committed to source control and are the same on all environments. & template is provided for database.php and needs be set up for each new environment. Bptionally7 bootstrap.local.php can be created7 based on a template7 which can override anythin. in core.php or bootstrap.php specific to the environment.

9e2loy#ent
"hether you use a custom shell script or one of the build packa.es like 'hin. or apistrano7 there are some common thin.s you:ll want to do when deployin. an update to your site. 9e"u3 5f you:re super paranoid about debu. accidentally bein. enabled7 you can force it to ?ero with a simple perl command. per6 -pi -e FsHde7ug', <0-L={1 Hde7ug', 0HgiF HpathHtoHappHconfigHcore1php Ca%he This is the sin.le most common issue with new deployments and also the bi..est frustration. 5f you:ve made chan.es to your database7 the model caches need to be rebuilt. The easiest way to do this is ;ust to delete them. r2 -rf HpathHtoHappHt2pHcacheH2ode6sH) Most likely7 you:ll want to delete your cached views as well. find HpathHtoHappHt2pHcacheHvie;sH T grep php T xargs r2 Yf 5:m usin. a sli.htly different synta9 here because this way will handle a lar.e number of files.

lternate .ethods
)eil rookes - http://www.neilcrookes.com/1++K/11/1K/runtime-confi.-in-cakephp-apps/ hris (art;es - http://www.littlehart.net/atthekeyboard/1++K/11/1K/handlin.-multipleenvironments-in-your-php-application/ Cafael Bandeira - http://rafaelbandeira*.wordpress.com/1++K/11/+3/handlin.-multipleenviroments-on-cakephp/ @;ell Bublit? - http://cakealot.com/1++K/11/environment-and-database-confi.-easy-cakephpdeployment/

<1

CakePHP Reser!ed Classes


,nlike some other frameworks7 ake doesn:t prefi9 its classes with the framework name. This may cause some conflicts if you try to create a .enerically named class or model that may already e9ist in the ake core. (ere are some of the class names you:ll need to avoid and some alternatives. Class Fa#e &pp ache onfi.ure ontroller =ebu..er =ispatcher Grror(andler #ile #lay #older 51+n 51Kn Model Multibyte Bb;ect Couter $aniti?e $ecurity $et $trin. Validation Pml PmlGlement Pml)ode <1 lternati!e &pplication7 $ite &sset7 $tash7 Cepo7 Treasury onf7 onfi.7 $ettin.s Boss7 =irector =ebu.7 Grror >ofer7 Cunner7 Gmissary Grror7 GrrorBroker =ata7 #ile(andler $calp7 G9coriate =irectory7 =ir7 "rapper -an.7 -an.ua.e Ce.ion7 -ocale $tandard7 &rchetype Mb Base7 Gntity 'lotter7 $i.naler 'urify7 lean

$hield7 >uard7 =efense Bunch7 >an.7 'ack Term7 $eFuence 'roof7 Verification

)ro# The Bakery ? nd -ther Pla%es@


There4s a lot of code in the ake'(' Bakery and althou.h there is an approval process7 some of it isn4t so .reat. Below is a list of some of the better entries.

Beha!iors
Slu33a"le By: Mariano 5.lesias -ink: http://bakery.cakephp.or./articles/view/slu..able-behavior =escription: This behavior lets your models act as slu.-based models7 useful for .eneratin. $earch Gn.ine friendly ,C-s. Gasy to install and easy to confi.ure. So&t 9eleta"le By: Mariano 5.lesias -ink: http://bakery.cakephp.or./articles/view/soft-delete-behavior =escription: This behavior lets you implement soft delete for your records in your models by introducin. a fla. to an e9istin. table which indicates that a row has been deleted7 instead of deletin. the record. Linka"le By: Cafael Bandeira -ink: http://.ithub.com/rafaelbandeira*/linkable/tree/master =escription: -inkableBehavior is the implementation to solve ontainableBehavior:s ine9tensibility7 comple9ity7 featurity and - mainly - its db usa.e.

Plu3ins
9e"u3Git By: Mark $tory -ink: http://thechaw.com/debu.%kit/wiki =escription: The ake'(' =ebu.@it provides a set of easy-to-use debu..in. information to your applications. 5t provides functionality for session and reFuest inspection7 $M- lo. inspection7 and benchmarkin.. This functionality is provided as a plu.in for e9istin. ake'(' applications. Fa#edS%o2e By: Aoel Moss -ink: http://.ithub.com/;oelmoss/cakephp-namedscope/tree/master =escription: This )amed$cope behavior for ake'(' allows you to define named scopes for a model7 and then apply them to any find call. 5t will automa.ically create a model method and a method for use with the findMethods property of the model.

<*

Hel2ers
sset Note: Packaged as a plugin By: Matt urry -ink: http://.ithub.com/mcurry/asset =escription: &utomatically combine and compact Aava$cript and $$ files. This helps speed up browsin. by limitin. the number of reFuests7 as well as reducin. the overall file si?e. AEuery :alidation Note: Packaged as a plugin By: Matt urry -ink: http://.ithub.com/mcurry/;s%validate =escription: This helper takes your model validation rules and converts them to Aava$cript so they can be applied in the client4s browser before submittin. to the server. Ht#lCa%he Note: Packaged as a plugin By: Matt urry -ink: http://.ithub.com/mcurry/html%cache =escription: ake4s core cache helper is .reat7 but the files it outputs are '(' files7 so it will never be as fast as strai.ht (TM- files. The (TM- ache (elper writes out pure (TM-7 meanin. the web server doesn4t have to touch '(' when a reFuest is made. This helper is for sites with hi.h traffic pa.es that have nothin. uniFue about the user on the pa.e. "orks .reat for C$$.

<2

Co2yri3ht

/ou are &ree4 to Share Y to copy7 distribute7 display and perform the work

to Re#i; Y to make derivative works 0nder the &ollowin3 %onditions4

ttri"ution. 8ou must attribute the work in the manner specified by the author or licensor /but not in any way that su..ests that they endorse you or your use of the work0.

Fon%o##er%ial. 8ou may not use this work for commercial purposes.

Share like. 5f you alter7 transform7 or build upon this work7 you may distribute the resultin. work only under the same or similar license to this one.

#or any reuse or distribution7 you must make clear to others the license terms of this work. The best way to do this is with a link to this web pa.e. &ny of the above conditions can be waived if you .et permission from the copyri.ht holder. &part from the remi9 ri.hts .ranted under this license7 nothin. in this license impairs or restricts the author4s moral ri.hts.

: :

This is a human-readable summary of the -e.al

ode /the full license0.

<3

Re!isions
:H.D I .ay H3C 2DD(
5nitial Celease.

:H.H I .ay H'C 2DD(


: : : :

#i9ed custom find methods to be prefi9ed with two underscores to prevent a conflict with the native find methods. &dded info about hris (art;es4 book. Moved settin. the user automatically to beforeValidate instead of before$ave. & few typos and code synta9 errors.

:H.2 I Aune H2C 2DD(


: : : : : : :

&dded route for prefi9 when mer.in. add and edit. TB -inksR Cemoved call to %%construct for definin. custom find methods. More typos. Cemoved redundant forei.n@ey option from ,ser model associations. &dded method for distin.uishin. between add and edit when mer.in. actions. &dded advanced options slu. handlin..

<<

You might also like