Maintaining an Application-independant Code Library
As a developper for my web company, I often write cake components/helpers/etc that I reuse often throughout my various client projects. For a while now I've been looking for a way to use and manage this code with CVS in a way that avoids duplication. I finally found the solution.
In trying to find a way to manage all my code snippets, I finally settled on a solution that I think merits everyone's attention since it's so simple, so maintainable and so useful.
This idea is as follows, I set up a code library on my filesystem (I use windows, but this will work anywhere) with the following file structure:
The idea here is that I have a copy of my cake app/ file structure where I can put code that I will reuse in all my applications. This method now will allow any files I put in this code library to be available in any cake application I make in the future.
The simplicity in this approach is outlined by this example; Suppose I made a "FileHandlerComponent", the use of this component would simply go as follows:
That's all!
To do this, first create the above code library file structure. Now create a file D:/codelib/cake/codelib.inc with the following contents (adjust directories appropriately):
Once that is complete, all you have to do to make this code library available to your cake app is open your /app/config/bootstrap.php and put the following line in there:
Now, when you enter the following code in your controller:
It will first check your application's /app/controller/components/ folder for a file_handler.php file. If it's there, it will use the local one, otherwise it will check in your code library to see if the file exists there.
The advantage of this is that now you can develop a library of application-wide software functionality using cake, and this library can be maintained via CVS or SVN, and no more copy-paste related synchronization issues!
This idea is as follows, I set up a code library on my filesystem (I use windows, but this will work anywhere) with the following file structure:
D:/codelib/cake/
D:/codelib/cake/app/
D:/codelib/cake/app/controllers/
D:/codelib/cake/app/controllers/components/
D:/codelib/cake/app/models/
D:/codelib/cake/app/models/behaviors/
D:/codelib/cake/app/views/
D:/codelib/cake/app/views/helpers/
D:/codelib/cake/app/plugins/
D:/codelib/cake/app/vendors/
The idea here is that I have a copy of my cake app/ file structure where I can put code that I will reuse in all my applications. This method now will allow any files I put in this code library to be available in any cake application I make in the future.
The simplicity in this approach is outlined by this example; Suppose I made a "FileHandlerComponent", the use of this component would simply go as follows:
- Save my file_handler.php component file in D:/codelib/cake/app/controllers/components/file_handler.php
- In any of my cake applications, I would simply put the line "var $components = array('FileHandler');" in a controller and the FileHandler component will be loaded properly.
That's all!
To do this, first create the above code library file structure. Now create a file D:/codelib/cake/codelib.inc with the following contents (adjust directories appropriately):
<?php
$PATH_TO_CODE_LIBRARY = dirname(__FILE__) . DS;
$modelPaths = array($PATH_TO_CODE_LIBRARY.'app'.DS.'models'.DS);
$behaviorPaths = array($PATH_TO_CODE_LIBRARY.'app'.DS.'models'.DS.'behaviours'.DS);
$controllerPaths = array($PATH_TO_CODE_LIBRARY.'app'.DS.'controllers'.DS);
$componentPaths = array($PATH_TO_CODE_LIBRARY.'app'.DS.'controllers'.DS.'components'.DS);
$viewPaths = array($PATH_TO_CODE_LIBRARY.'app'.DS.'views'.DS);
$helperPaths = array($PATH_TO_CODE_LIBRARY.'app'.DS.'views'.DS.'helpers'.DS);
$pluginPaths = array($PATH_TO_CODE_LIBRARY.'app'.DS.'plugins'.DS);
$vendorPaths = array($PATH_TO_CODE_LIBRARY.'app'.DS.'vendors'.DS);
?>
Once that is complete, all you have to do to make this code library available to your cake app is open your /app/config/bootstrap.php and put the following line in there:
include('D:'.DS.'codelib'.DS.'cake'.DS.'codelib.inc');
Now, when you enter the following code in your controller:
var components = array('FileHandler');
It will first check your application's /app/controller/components/ folder for a file_handler.php file. If it's there, it will use the local one, otherwise it will check in your code library to see if the file exists there.
The advantage of this is that now you can develop a library of application-wide software functionality using cake, and this library can be maintained via CVS or SVN, and no more copy-paste related synchronization issues!

In this case read the notes in your app's config/bootstrap.php file. Cake already has a way of supporting this.
If you do insist on doing this with a prewritten script, then vendors/codelib.php combined with the proper App::import() call within bootstrap.php, using the available methods as described in bootstrap.php, is the best way to go.
Generally, yes. However, I'm just using it in the bootstrap so that instead of having to copy paste all those $modelPaths, $behaviourPaths, etc.. in every app you use, the include makes it much simpler. It's also way more maintainable this way since you can make changes to your code library without breaking every app that uses it.
There may be a performance hit, but this is for your dev environment. In the case of production apps, you should probably be pasting the code from your dev library into your app folders anyways, just to avoid all those file reads. That's what I do anyways.
[Edit : In a nutshell, this is done for simplicity in your dev environment. I wanted a way to use this code library by just pasting one line. It's quite effective this way.]
Also, the $ is missing in the code example for adding a component. Should be:
var $components = array('FileHandler');The best solution is just to use vendor branching. Keep your libraries in their own repository, with local versions in each of your applications. The newest library files can be merged into each application as you need.
Felix has a handy screencast of setting this up. His method is slightly over-complex, but it achieves the job well.
http://www.debuggable.com/posts/screencast-using-vendor-branching-and-cakephp:480f4dd6-6cac-44cb-b685-4d6bcbdd56cb
So you're saying that a version of the library should be checked out for each project? I guess that makes sense, but the above method is still applicable so you don't have to have a second repository existing in your app/ directory.
The whole point is that if my app/ directory is in CVS/SVN, then I can't manage my PaypalComponent across 9 different applications via CVS/SVN unless it's taken out of the app/ folder.
An alternative also is to include versioning parameters in all your function calls. Don't know if that would be overkill or not.
- I have a similar situation - components/helpers that are used in every project for the last couple of years.
- The version used in a site from 18 months ago is nothing like the up-to-date version. That old site needs to remain in a completely working and tested state in case updates need to be made.
- Having a shared library of code, which could change at any time independently of a project using that library, is a minefield. You will have to go back and test EVERY past application with every library change - completely impractical.
- With your suggested method, in 2 years after you've made 25 more projects and your library has evolved substantially, is this old project going to be in exactly the same (working) state as you left it?
- Vendor branching lets each application have, in their own repository, the exact version of the library that is known to work.
- Vendor branching lets each application have some unique modifications to these libraries, if necessary.
- Vendor branching allows you to merge in the updated library, with all the good stuff that SVN merge lets you do, any time you are ready to do so (and test).
- Vendor branching is not SVN:extern, and is not just copy & paste. It's better than both.
I like to use this method and then copy/paste my plugins only if I release to an environment that can't have my library installed, or an environment where I don't want the client to have my full library.
1. Try to have most of your reusable stuff as plugin, so everything remains in the folder.
2. When needed, copy paste the folders etc.
I have several reusable plugins like Comments, Star Rating, and many more..
Earlier, I was using a very similar trick like yours.
Any file structure will work, so long as you adjust the .inc file. You can really set it up the way you like, I just preferred to have a cake/app directory because I have other code in my codelib/ and codelib/cake folders that I don't want to easily include within my cake apps.
Thanks for the feedback, I have updated the .inc file in the article with your suggestion.
$PATH_TO_CODE_LIBRARY = dirname(__FILE__) . DS;
(Makes it a little more portable.)
D:/codelib/cake/
D:/codelib/cake/controllers/
D:/codelib/cake/codelib.inc
Or even:
D:/cakelib/
D:/cakelib/controllers/
D:/cakelib/codelib.inc