Home

Thu, May. 8th, 2008, 01:37 am
Using libchart as a cakephp vendor application

First off, in cake 1.2, you vendor() is depreciated, so you'll have to use App::import('Vendor', ... ); A link on its usage can be found <a href="http://cakebaker.42dh.com/2008/03/26/loading-vendor-files/">http://cakebaker.42dh.com/2008/03/26/loading-vendor-files/</a>
First off, we want to unpack libchart tarball, and put the libchart directory in /app/webroot/vendors/.
The basic steps for creating a graph are:
  1. Create an action that will *just* fetch data
  2. hand the data over in a predetermined fashion to the view.
  3. Then the view will call a helper function which will do the actual graphing (libchart calls). 
So for example, I created an action in my controller which displays a number of fruits. Important things to note are the first three lines, this turns off all verbose debugging, and default layouts which will get in the way of the PNG. The "predetermined" fashion is that my helper is going to accept a chart title, and an array of data in the form of x-labels => y-vals.

class FruitsController extends AppController {
    var $helpers = array('Html', 'Form', 'Javascript', 'Graph');
...
    function chartFruitTotals($id = null, $rndm = null) {
        $this->layout = null;
        $this->autoLayout = false;
        Configure::write('debug', '0');

        // do some database stuff here to get data. I'll use a prefilled array
        $chartData = array ('apples' => 3, 'pears' => 4, 'oranges => '1', 'peaches' => '2');
        $chartTitle = 'Fruit Totals';
        $chartData = $alertTotals;
        $this->set(compact('chartTitle', 'chartData'));
    }
...
}


The view is rather simple. Just call the GraphHelper's method which will do the charting.
<?php
    $graph->showChart($chartTitle, $chartData);
?>


The helper method is where the magic will be. Since libchart.php in its own directory (two directories deep) we'll be using the following syntax: App::import('Vendor', 'libchart', array('file'=>'libchart'.DS.'classes'.DS.'libchart.php')); where DS is the directory separator. I just ended up copying a barchart demo file that came with libchart and just slightly modifying it.
<?php
    App::import('Vendor', 'libchart', array('file'=>'libchart'.DS.'classes'.DS.'libchart.php'));
    class GraphHelper extends AppHelper {
...
        function showChart($chartTitle, $chartData)
        {
            header("Content-type: image/png");
            $chart = new HorizontalBarChart(300, 250);

            $dataSet = new XYDataSet();

            foreach ($chartData as $xLabel => $yValue) {
                $dataSet->addPoint(new Point($xLabel, $yValue));
             }

            $chart->setDataSet($dataSet);
            $chart->getPlot()->setGraphPadding(new Padding(5, 15, 15, 100));
            $chart->getPlot()->setLogoFileName(null); // get rid of the annoying logo

            $chart->setTitle($chartTitle);
            $chart->render();
        }
...
?>

Now, what that will do is spit out a raw PNG file to that particular view. If you would like the chart to appear as an <IMG> anywhere in your pages, you probably want to do a component or something similar that does the same as the helper and just $chart->render($tmpfile) it out to "tempnam", or "tmpfile", and a wrapper in the img/ directory @readfile's the tmpfile/tempnam. To be safe, make sure any reads are escaped correctly. There are other ways to skin that cat, but at this point it's just plain ol' php.

Tue, May. 6th, 2008, 11:20 pm
Set::merge, Set::extract and further goodies

So, I found a couple of cool demos of the Set class (and this time I understand more of them).
The best place to look at how the Set class works is in the 'set.test.php' test case. It's a bit more obvious than reading the source.
In any case, check out this site for some nice demos: http://debuggable.com/posts/cake-12s-set-class-eats-arrays-for-breakfast
And also check out this site: http://www.cakephp.nu/quick-tutorial-cakephp-set-class-part-1

Thu, Apr. 24th, 2008, 12:30 am

OK, so I've been hunting all evening for something that's been in front of my face the entire time.
All I wanted to do was paginate() the result of a specific query... THAT'S MY PROBLEM. I was asking the wrong question. I wanted to paginate the result of a particular condition.
I went as far as creating the specific paginate query in the model (which is where complex paginate queries go, not the controller). Then I asked myself, if I can pass it a $condition, why can't I just do that in the controller with the default paginate().
THEN IT HIT ME! $this->paginate is a fancy kind of Model::findAll(). The same options you can give findAll() you can give to paginate().
OK, so...
  1. You can set some defaults to $this->paginate() as a 'var' in the controller called 'paginate'. (See previous post) This will apply to all vanilla $this->paginate() calls.
  2. $this->paginate() returns what $this->Post->findAll() would return.
    1. It also accepts all options findAll() would accept.
    2. Use it as you would findAll(): http://book.cakephp.org/view/448/findall
Putting it all together, five hours ago, in my "non index action" all I needed to do was:

$conditions = array('Journal.incident_id' => $incidentId);

// $journals = $this->Journal->findAll($conditions);
$journals = $this->paginate($conditions);
$this->set('journals', $journals);
You know, for all the time CakePHP is 'saving' me by auto-generating this code, it's taking back by lack of cohesive well written documentation. Does that mean I give up... ugh, dzang, no... when I 'get' it and it works... it REALLY WORKS! :)

I just wish I had more time. I have to get this site done by May 15.

Wed, Apr. 23rd, 2008, 10:28 pm
$paginate fields

In cake 1.2 when setting the "displayable" fields of the model in question, use the related model's name.
class JournalController extends AppController {
...
var $paginate = array(
'Journal' => array(
'limit' => 25,
'fields' => array(
'Journal.id',
'Incident.id',
...

Not 'Journal.incident_id'

Wed, Apr. 9th, 2008, 09:14 pm
replacing generateList()

OK, so to get more detailed into this. From the controller you want to pass along the dropdown list, just do this. You have two models "Incident" and "User". User model contains "firstname" and "lastname" field, but you want them to show up as "firstname, lastname" when the select is automagically generated.:
class IncidentsController extends AppController { 
...
	function add() {
		...
		$users = $this->Incident->User->find('all',
			array('fields' => array('id','lastname','firstname')), 0);
		$users = Set::combine($users,
				'{n}.User.id', /* key */
				array('{0}, {1}', /* formatting, note the comma in the middle */
					'{n}.User.lastname','{n}.User.firstname')); /* mapping to the format */
		...
		$this->set(compact('users', ... )); /* if it has just $this->set('foo'); replace with compact */
	}
...

Repeat those $users lines as often as you need to pass it to $this->set(); however, I would create a local private function and call the private function.
...
        function __genUserList() {
                $users = $this->Incident->User->find('all',
                        array('fields' => array('id','lastname','firstname')), 0);
                $users = Set::combine($users,
                                '{n}.User.id',
                                array('{0}, {1}','{n}.User.lastname','{n}.User.firstname'));
                return $users;
        }
...
	function add() {
		...
		$users = $this->__genUserList();
		$this->set(compact('users', ... ));
	}
...

Thu, Apr. 3rd, 2008, 09:02 pm
cake is a double edged sword

The one thing that got me coding in QT and taking the plunge into the deep end, was the detailed documentation and crosslinked documentation /within the detailed definition/ as well as in the definition header, etc.

Well, I have seen what CakePHP is possible, but for all of it, there's definitely a lack of documentation. And even if there /is/ documentation, a lack of a stable 1.2 version (on which most of the documentation, official and spread throughout the web is based) makes it a hair pulling experience if you want to try some feature out.

Hopefully some day.

Tue, Apr. 1st, 2008, 11:32 pm
Internationalization in CakePHP

Here:
http://groups.google.com/group/cake-php/browse_frm/thread/1edcbbaed20657b9/13afbe9841b91314?#13afbe9841b91314
And a better one here: http://aaronthies.com/wp/2007/08/03/cakephp-12-i18nl10n/
INTERNATIONALIZATION

- at top of controller: uses('L10n');
- create /app/locale/eng/LC_MESSAGES/default.po (French is fre/fra)
http://www.loc.gov/standards/iso639-2/php/code_list.php
- create entries in default.po as such:

msgid "close_window"
msgstr "Close"

msgid "where_pin"
msgstr "Where is my PIN?"

- call translations in view: ) ("close_window"
is msgid from default.po)
this is going to echo out the msgstr for the given msgid, else it
wil display the msgid given
use __("button_submit", true) to return the value as opposed to
echoing it

Thu, Mar. 27th, 2008, 08:51 pm
CakePHP Bug in beta release

The beta release of CakePHP has a MAJOR BUG affecting the HABTM relationships. I tried the fix in that link, and there were still issues with table associations. I downloaded the nightly for 1.2.x.x_27.03.2008 and copied the "cake" directory over to my project, and everything seems to be working OK now.

Honestly, I think that a bug of that type should not be in a "beta" release. They should release an updated tarball. Someone like me, learning it for the first time, will take hours to figure out what is going on, and whether I am just "doing something wrong." In any case, the nightly tarball hasn't broken anything yet, so I would recommend it.

Tue, Mar. 25th, 2008, 11:16 pm
CakePHP Validation

So from 1.1 to 1.2, validation changed too. Apparently 'alphaNumeric' validation is just that (26 letters, and 10 arabic numerals). The following worked for a required field, which could not be left blank, but would allow spaces and alphanums:
'fieldName' => array( 
	'rule' => array('custom', '/^[a-z0-9 ]*$/i'), 
	'required' => true, 
	'allowEmpty' => false )

Thu, Mar. 20th, 2008, 12:25 am
Set::combine

More on Set::combine (as close to documentation as it gets).
http://www.thinkingphp.org/2007/02/24/cake-12s-set-class-eats-arrays-for-breakfast/

Thu, Mar. 20th, 2008, 12:15 am
generateList() disappeared in CakePHP 1.2

After a couple of days of messing about with CakePHP, I have decided to move on to 1.2 (beta). There are a lot more functions in the newer version than the old. The problem is that there's even LESS documentation.

The Model::generateList() disappeared in CakePHP 1.2.x :( This was a handy function to generate key/value pairs from tables for stuff like a drop down, or radio button. The following two calls should work:
var $uses = array('Model1', 'Status');
...
$tmpVals = $this->Status->findAll(array('context' => '= users'));
$tmpVals = Set::combine($tmpVals, '{n}.Status.id', '{n}.Status.name');
This is assuming that your Status table contains the fields 'id', and 'name' which would be the <.. value= ..> and the plain text description, respectively.

Wed, Mar. 19th, 2008, 01:41 am
CakePHP

CakePHP rules.
Note. If you have a user table with a status id, and a status table with id/desc, then in the user model do a user belongsTo status association.

Fri, Aug. 24th, 2007, 12:11 pm
Ruby on Rails vs CakePHP

Interesting article as to "why" RoR vs (cake)PHP. http://jimmyzimmerman.com/blog/2007/05/why-i-prefer-ruby-on-rails-over-cakephp.html