Archive of December 2008

CakePHP: Watch Out for the compact() Function

Okay, so maybe this caveat isn’t specific to CakePHP since the function is a native PHP function, but I just spent the past hour trying to figure out why variables that I clearly set in my controller weren’t available to my view. I’ll never get that hour back, but maybe I can keep someone else from losing it. As far as I’ve been able to tell, this particular behavior isn’t documented, but I haven’t done any kind of exhaustive search.

The compact() function exists as a shorthand function to create an array of variables. The PHP documentation does a nice job of detailing the function and providing a few simple examples, so I won’t belabor that point. Within CakePHP, it’s a handy shortcut for passing variables from a controller to a view. In my case, I have a vendor application form with a few variances that I need to manage. Without using compact(), a snippet from the action method of my VendorsController would look like this:

public function index() {
   …
   $vendor_type      = ‘Food’;
   $application_type = ‘Commercial’;
   $states           = $this->Vendor->Address->State->find ( 
      ‘list’, 
      array ( ‘order’ => ‘State.title’ ) 
   );

$this->set ( ‘vendor_type’, $vendor_type ); $this->set ( ‘application_type’, $application_type ); $this->set ( ‘states’, $states ); … }

That’s not an intolerably verbose example, but it’s easy to see how it could become so. Being the laconic kind of guy that I am, though, I decided to use the compact() function to cut down on the verbiage. Now my action code looks like this:

public function index() {
   …
   $vendor_type      = ‘Food’;
   $application_type = ‘Commercial’;
   $states           = $this->Vendor->Address->State->find ( 
      ‘list’, 
      array ( ‘order’ => ‘State.title’ ) 
   );

$this->set ( compact ( ‘vendor_type’, ‘application_type’, ‘states’ ) ); … }

That’s a little better, but there’s a caveat.

Using this example, I went to my view code and tried to access my $vendor_type variable only to get an undefined variable error. After working my way through various debugging techniques, I pulled out the big gun. In my view, I dumped the output of PHP’s get_defined_vars() function and found that my $vendor_type variable had been renamed to $vendorType. I don’t know if the change was made by CakePHP or by PHP itself, but I didn’t expect it (nor do I have any reason to believe that I should have expected it) and the difference is as fatal to an application as it is obvious.

CakePHP: Adding Dynamic Content to Layouts

For the past few months, I’ve had this lingering item on my TODO list to Learn CakePHP. When that item was added to the list, we had decided to use it as our default framework for building new apps at work and I wanted to at least be aware of its basic capabilities, techniques and toolkits. Unfortunately, I don’t get as much time as I’d like to put my head down and write code at work, so this item has been taunting me until this week when some time presented itself.

I come into this brand new to CakePHP, but as an experienced web and OOP developer. I have certain expectations of encapsulation, elegance and maintainability that I want to bring to my CakePHP project. As I run across scenarios where I don’t think that such elegance is obvious, I’ll document the solution I used here. Maybe someone else can benefit from my experience and maybe I can benefit from that of others.

The Project

Since I learn most effectively by doing, I needed a project. The project I chose was a rebuild of a site I did about a year ago; I was familiar with the needs as well as the things I would have liked to do differently, had there been enough time.

For the site I’m building, I have not only a header and a footer, but also a sidebar. The site has navigation menus in each of these areas of the template that I wanted to be data driven and several menu items are shared across menu locations. Not an uncommon need, to be sure, but I wasn’t sure how to handle it with CakePHP because layouts don’t have models – or controllers – of their own to access the data and this layout would be shared by every page in the site – I didn’t want to add the logic to retrieve and manipulate menu data in more than one place.

The Scenario

The scenario, then, is the need to place dynamic content within a layout. Without going into excruciating detail, a CakePHP layout is analogous to a template in a CMS. It’s the part of the page that is fixed and usually shared across multiple pages of a site. On robwilkerson.org, for example, the layout would include the header, the footer and the sidebar on the right. Everything else is the page content or, in CakePHP’s MVC-speak, the view template.

The Data

The backbone of a data driven solution, of course, is the database. For my menus problem, I created three tables: nav_menus, nav_menu_items and nav_menu_items_nav_menus following all of the conventions of CakePHP so that everything would work without any more effort than necessary. The last table, of course, is a linking table to identify which items exist on which menus. The many-to-many relationship it enables is what allows a single menu item to appear on more than one menu.

The Models & Controllers

With the database prepared, I needed to provide access from my application and this is where CakePHP and most other frameworks I’m familiar with really shine. Using CakePHP, I just “baked” models and controllers using the command line utility:

$ cd path/to/cake/console
$ ./cake bake model nav_menu
$ ./cake bake controller nav_menus
$ ./cake bake model nav_menu_items
$ ./cake bake controller nav_menu_items

That’s it. A few simple command line requests and I had all the files I needed. All that was left to get this scaffolded up was to define the associations between the models I’d just created. To do so, I opened each model file (located in app/models/) and added a single line of code to each. To app/models/nav_menu.php, I added the following:

public $hasAndBelongsToMany = array ( 'NavMenuItem' );

Similarly, I added the following to app/models/nav_menu_item.php:

public $hasAndBelongsToMany = array ( 'NavMenu' );

That’s all I needed to do to be able to create new menus and menu items. By requesting http://localhost/nav_menus/add, I was able to use CakePHP’s scaffolded interface to create new menus. Likewise, by accessing http://localhost/nav_menu_items/add, I could create new menu items and assign them to the appropriate menus.

Sharing the Logic

The non-obvious question in my mind was how do I get this dynamic data displayed on a layout? A layout doesn’t have its own controller and I sure has hell don’t want to have to tell every controller how to render my navigation. Inheritance to the rescue.

CakePHP’s controllers all extend the AppController class, so I dropped my menu logic there:

class AppController extends Controller {
   public $uses = array ( ‘NavMenu’ );

public function beforeRender() { /** * Populate and expose menu data */ $raw_menus = $this->NavMenu->find ( ‘all’ ); $menus = $this->NavMenu->unobfuscateMenus ( $raw_menus ); $this->set ( ‘nav_menus’, $menus ); } }

In my mind, this is perfectly reasonable since this logic really will be used on every single page request; there are no CPU cycles being wasted.

An Associative Recordset

If anyone’s wondering about the unobfuscateMenus() method, I created a custom method in my NavMenus model that would allow me to access and key on my menus as an associative array rather than as an indexed array. I have three menus: Primary, Secondary and (surprise!) Tertiary. By default, CakePHP exposes these as $nav_menus[ 0 ][‘Primary’], $nav_menus[ 1 ][‘Secondary’], etc. I wanted to be able to key on the menu name, though, so that in my layout, I had the ability to access the precise menu I wanted when I wanted it.

The unobfuscateMenus() method reorganizes the array slightly so that I can access $nav_menus[‘Primary’] when I want to display the primary menu without looping over all menus and testing for the menu name. This should become more clear in the code that renders the menu below.

With the code added to the AppController class, all of my controllers and, by extension, all of my layouts will have access to my menu data. Since I have several menus on my layout, I not only wanted to share the logic, but also the output.

Sharing the Output

Duplicating presentation code is almost as distasteful to me as duplicating business logic, so I created a navigation element that would accept my $nav_menus array and iterate over each item to create an unordered list. The menu array whose items will be displayed is passed to the element as the $menu variable.

<ul>
   <?php $i_item = 0; ?>
   <?php foreach ( $menu['items'] as $item ): ?>
   <?php
      $i_item++;
      $item_class = '';
      if ( $i_item  1 ) {
         $item_class = ' class="first"';
      }
      else if ( $i_item  count ( $menu['items'] ) ) {
         $item_class = ' class="last"';
      }
   ?>
   <li<?php echo $item_class; ?>>
      <a href="<?php echo $item['target_uri']; ?>" 
         title="<?php echo $item['description']; ?>"
         ><?php echo $item['title']; ?></a>
   </li>
   <?php endforeach; ?>
</ul>

Wiring It All Together

My logic, data and presentation code are all in place now, so the only thing left is to apply the context – the layout. In my layout markup, I included the following code to display my primary menu:

<?php echo $this->element (
      'navigation',
      array (
         'id'   => 'primary',
         'menu' => $nav_menus['PRIMARY']
      )
   );
?>

And for my secondary menu:

<?php echo
      $this->element (
      'navigation',
      array (
         'id'   => 'secondary',
         'menu' => $nav_menus['SECONDARY']
      )
   );
?>

Key Points

Here’s what I like about this solution, in no particular order:

  1. One database query. I have three menus and I certainly could have retrieved each menu individually. Whenever it’s feasible, though, I like to avoid querying the database. In my experience, the database is almost always the bottleneck for an application; the less I have to communicate with it, the better. With a little application logic (the unobfuscateMenus() method), I get all the benefits of three queries at the performance cost of just one.
  2. It will scale. If I need six navigation menus, all that’s required is a little more data. The code can handle the additional menus without any changes.
  3. There’s no waste. I’m very wary of dropping code in a super class, but in this case it works. The AppController class is loaded on every request and the logic it includes is used on every request. Symmetry.
  4. Encapsulation of presentation. Within the element, there’s no logic that isn’t used exclusively for presentation. Because I unobfuscated the menus array, I’m able to pass in only the menu that needs to be rendered by the element at the time it’s called.

Give Firefox Fangs

Fangs logo

Fangs is a Firefox extension that presents a web page in a manner similar to how it would be read by a screen reader. I’m not an accessibility guru, nor am I and accessibility geek. I try to hit the high points of accessibility like employing skip navigation, alt tags, meaningful link text, etc. when I build sites, but at the same time I have to confess that I’ve never dug much deeper than that. As far as I can tell, Fangs doesn’t offer much in the way of education, but what I really like is that it provides another way of visualizing the structure of my content. Moreover, it’s a practical visualization since it emulates the way a screen reader will read that content rather than some kind of basic block diagram.

Although, my impression so far is that Fangs doesn’t offer much in the way of accessibility information or evaluation, there are some hints about how to effectively use Fang on the FAQ. Even with limited use so far, I’ve seen enough to know that there’s a place in my development toolbox for this extension.

Make Linux Just a Little Sexier

Granted, I’m a Mac user, at least at home, but I like to think I’m not a total fanboy. When I moved to Linux at work and began customizing my machine, I saw countless recipes with instructions for skinning Linux to look like a Mac (or maybe there’s just one and everyone in the free world is linking to it). I don’t get that. Linux isn’t a Mac. A penguin in a tux is still a penguin, right? The fact is, I like my operating systems to look like what they are and, besides, I’ve never had much luck with Linux themes. There’s always something that gets left out and kind of ruins the whole effect.

The honest truth is, I kind of like the Linux aesthetic. Most of it, anyway. I’m not, however, a fan of the default fonts and I thought that installing Mac fonts might be just the thing to dress up the penguin without overdoing it. Turns out that they are.

Try it. Here are my settings:

My Ubuntu font settings

Fix Title Bar Issues in Ubuntu 8.10 (Intrepid Ibex)

Towards the end of November, I upgraded (read: completely rebuilt) my Linux partition from 8.04 (Hardy Heron) to 8.10 (Intrepid Ibex). I’ll have more to say about the latter in a future post, but after the upgrade I was immediately faced with two problems. One, the inability to establish a VPN connection, that was critical to my ability to use this as my primary work computer and the second, an issue with my title bars that, though extremely annoying, was really just aesthetic.

The solution to the VPN issue is detailed in an earlier post and today I finally got around to fixing the title bar issue. It took me a while because, though the solution itself is relatively simple, I had a lot of trouble finding the entire solution in one place. Thus, the birth of this post.

The Problem

They say that a picture is worth a thousand words, but unfortunately I neglected to take a screenshot before fixing the problem. Nonetheless, I’ll try to keep my description well under a thousand words.

Frequently and inconsistently, my active title bar would go grey and the appearance would fracture. I don’t know how to describe it any better except to say that if Picasso himself had painted a title bar in shades of grey, it would have probably looked much like mine. Cubist, indeed.

The problem, in a nutshell, is with the NVIDIA video driver. If you don’t have an NVIDIA video driver, you probably don’t have this problem. Intrepid Ibex ships with three proprietary NVIDIA driver versions: 96, 173 and 177. I don’t remember which was installed by default, but I encourage you to trust me when I say that not one of them worked.

The Solution

First, of course, you need the right driver. I read somewhere that version 180.06 worked as expected so that’s the one I downloaded. I’ll save you the suspense and tell you that this version did work for me. It can be downloaded from the NVIDIA site, but be sure to select the 32 bit or 64 bit version, as appropriate. I downloaded it to my desktop. Once you have the appropriate driver:

  1. Close everything except for a terminal window. I do mean everything because you’re about to stop the X Windows process.
  2. In the terminal window, type sudo killall gdm and hit Enter.
  3. Once the screen goes black, hit Ctrl+Alt+F1 to switch to a new terminal.
  4. You should see a terminal prompt so switch to the directory where you downloaded the driver file. My command was cd ~/Desktop.
  5. It may be sufficient to just use sudo here, but just to be sure, I dropped into a root shell by typing sudo -s and hitting Enter.
  6. Execute the driver file. If you didn’t rename the file, the terminal command should look something like this: ./NVIDIA-Linux-x86_64-180.06-pkg2.run.
  7. Follow the prompts through the installation process. You may see some scary messages about the driver having “no precompiled kernel interface”, but allow the install to do what it needs to do (it’ll ask to check for one and, failing that, it will ask to compile its own). I let it go about its business and had no problems at all.
  8. Once the install is complete, you can probably just type startx and hit Enter, but again, I wanted to be really, really sure, so I did a full reboot. If you used sudo -s as I did and choose to simply restart X, I’d recommend logging out of the root shell first or else you’ll start X as root.

That’s all there is to it. So far, everything looks good, but I have noticed one thing that looks odd to me:

Ubuntu Hardware Drivers Window

Seems Ubuntu thinks I’m still using version 177. As I said in my last post, Ubuntu is pretty solid, but not everything “just works”.