Tuesday, April 6, 2010

Using JSON in CakePHP 1.2

Recently, I was doing a project that required me to make two drop-down menus related to each other. The scenario was if you chose a company from a list of drop-down list of companies, the subsequent contacts drop-down would only be populated with contacts specific to the company chosen. I knew from past experience that I would be able to do this if I could get the contacts served to me as JSON. Then I stumbled upon this article that explains in a very simple way how to get CakePHP 1.2 (NOTE: This works with 1.3 as well) to return JSON via a view.

http://www.pagebakers.nl/2007/06/05/using-json-in-cakephp-12/

One point of note, it was a little confusing where to put the .json extension on the URL to make this work correctly. Just so there is no confusion, this is always at the end of the URL. For my use in getting the contacts I needed the URL was "/contacts/view/1.json" where 1 is the company ID.

Another link treasure I discovered during this process was this link from Remy Sharp's blog:

http://remysharp.com/2007/01/20/auto-populating-select-boxes-using-jquery-ajax/

Friday, March 19, 2010

virtualFields in CakePHP 1.3

I found a really handy feature in CakePHP 1.3's Model Attributes. Before 1.3, you were only able to utilize one field in your model with the variable $displayField as illustrated in this link in the 1.2 book.

http://book.cakephp.org/view/438/displayField

This field, for those who aren't aware, are typically used for display of lists within your views. This is usually fine but what about cases where you want to display a concatenated field like "firstname lastname"? Enter CakePHP 1.3's new virtualFields Model attribute. This is a great addition in my book. As most things go with CakePHP, I accidentally stumbled on it. I noted in the 1.3 book included this comment under the information about displayField. "Multiple field names cannot be combined into a single display field. For example, you cannot specify, array('first_name', 'last_name') as the display field. Instead create a virtual field with the Model attribute virtualFields."

I navigated over to the description of this new virtualFields attribute and found exaclty what I was looking for. I placed the following code into my model that I'm currently working on and now all my dropdown menus magically show the my user's first name and last name magically together exactly the way I want them to show up.



class Contact extends AppModel {
var $name = 'Contact';
var $virtualFields = array(
'fullname' => "CONCAT(Contact.firstname,' ',Contact.lastname)"
);
var $displayField = 'fullname';
/* Other model code taken out for brevity. */
}


Note that now I can utilize the new virtual field 'fullname' as the single-dimensioned $displayField variable. I'm loving that because not only can this be utilized for things like lists, but also these variables can be used as a normal part of the model attributes. Remember though, as indicated in the 1.3 book, "Fields added to this property will be read as other fields in a model but will not be saveable."

Wednesday, June 10, 2009

CakePHP Flex and the CpAmf Plugin

Been trying to dive into more RIA development by learning Flex. In the process, I came across the following articles:

Flex remoting with cakePHP - CpAmf plugin

CpAmf Flex Example

In this article it talks about VO (Value-Object) mapping. I was able to download the demo and get it all working great. However, when I tried to tackle this myself in my own application, I came up frustrated. The reason for this frustration was that apparently I don't read very well. In the article from The Bakery it indicated the following.


In our model we create an afterFilter method, and use the cakePHP built in Set::Map() function to convert the associative array to an object (or array of objects). We use generic class here (php's dummy class 'stdClass'), set the _explicitType property of all objects, and unset the _name_ property (which is set by Set::Map() method), because we don't need this property in our flex class. This approach allows us to change the model, and the corresponding flex class without the need for changing the vo classes (we don't even need to create them).


Well, after two days of fooling around with my controller to make this work, I ended up digging into the Model code from the demo project and found this little snippet:


function afterFind( $results )
{
//Convert the results to objects
$resultObjects = Set::map( $results );
foreach ( $resultObjects as &$item )
{
// Set _explicitType for all objects,
// for correct mapping the object in flex
$item->_explicitType = $this->name;

// We does not need the _name_ property, which is set automatically
// by the Set::Map() function when we use it without a class name
unset ( $item->_name_ );

}

return $resultObjects;
}


Had I been reading the original article correctly, I would not have wasted all this time trying to figure this out.

Thursday, January 22, 2009

Quick Tip - Doing Ad-hoc Joins in Model::find()

Where was this 5 days ago when I needed it for a project? This is a whole lot better than using bind() and unbind().

Quick Tip - Doing Ad-hoc Joins in Model::find()

Friday, November 21, 2008

Sementation fault with $uses variable in app_controller

Tonight, my Apache instance (OSX 10.5.5 Apache/2.2.9 (Unix) mod_ssl/2.2.9 OpenSSL/0.9.7l DAV/2 PHP/5.2.5 ) threw a segmentation fault when I tried to use one of my Models from the app_controller. In my code, this is how I had this setup:


class AppController extends Controller {
var $helpers = array('Html', 'Javascript', 'ForLayout', 'Tree');
var $components = array('Session', 'obAuth', 'Cookie');
var $uses = array('Contentpage'); /* THIS IS THE OFFENDING LINE */
...
}

I discovered this by systematically commenting out every piece of code that was implementing my Contentpage model. When I commented out the $uses variable, the segmentation fault went away.

So now my code reads like this to remind me.


class AppController extends Controller {
var $helpers = array('Html', 'Javascript', 'ForLayout', 'Tree');
var $components = array('Session', 'obAuth', 'Cookie');
/*
* WARNING!!! THIS CAUSES A SEGMENTATION FAULT!! DO NOT USE!!!
* var $uses = array('Contentpage');
*
*/
...
}


I've seen some issues in the CakePHP trac regarding segmentation faults, but nothing of this exact same nature.

As a side note, I thought I would try to use it from another controller. Initially this worked fine, but when it ran functions inside of obAuth, I got very unpredictable results. I'm still not really sure exactly what the nature of that error was. While I was debugging this, the segmentation fault started up again.

At any rate, I found a way to work around this so it doesn't hinder my work by putting a different layout on the controllers and actions that I originally wanted this functionality to execute on. However, I'm not sure what I'll do if I need a Model available to the app controller.

Has anyone else had this issue?

Wednesday, November 19, 2008

Define your own "for_layout" variables

Was looking for a way to add variables called $keywords_for_layout and $description_for_layout to provide meta data in my default layout page and I came across this from cakebaker. After I fought off the urge to set these fields in my controller all went well.

Define your own “for_layout” variables - cakebaker

Thursday, November 13, 2008

Acl/Auth vs. obAuth

Just spent the better part of a frustrating week dealing with the over-discussed and under documented Acl/Auth built into CakePHP 1.2. I finally threw up my hands in disgust even after I figured out how the whole system worked. I wanted to have granular control over my actions in my controllers and after I finally figured out that I had to have "all" my actions listed under my aco tree, I still got errors when I tried to log out of the application that my "logout" task was not properly mapped. All that time to figure out how to do this the out-of-the-box, Cake way only to hit yet another wall.

Finally, I passed the point in my project where I can no longer be held up with this and reverted to my old and dear friend obAuth. I had this configured in my 1.1 projects so I had all of this functionality outlined and in about 30 minutes, I had the authentication working with this simple batch of code in my app_controller.

	....<br />	var $components = array('obAuth');<br />	function beforeFilter() {<br />		$this->obAuth->startup($this);<br />		if(isset($this->params['admin'])){<br />			switch($this->params['controller']) {<br />				case 'groups':<br />				case 'pagetypes':<br />				case 'states':<br />				case 'countries':<br />				case 'types':<br />				case 'challenges':<br />					$this->obAuth->lock(array(ADMINISTRATOR)); // This is defined in core.php<br />					break;<br />				default:<br />					$this->obAuth->lock(array(ADMINISTRATOR, WEBMASTER)); // These are  defined in core.php<br />			}<br />			<br />		}<br />	}<br />...<br />


If I have this to do over again, I would probably look into setting the permissions on the controller level with ACL and then using the "controller" or "crud" as the action on $this->Auth->authorize. This is the way most people that have gotten it to work are successful with it. The most helpful site on this is Aran Johnson's site at Aran World. His sample kitchen web site gave me a lot of useful tips on how to work through the Auth part of this and as long as I stayed close to the demo code and didn't stray, I could make it work.