Posts tagged with “”

Learning CakePHP: Validation

I started developing for the web sometime in 1995 while I was still in the military. I “turned pro” in 1997. At that time there were two methods of data validation: client-side and server-side. Yeah, I know, that’s still all we have, but I think that Ajax has helped to blur the line between the two – perceptually, I mean, not technically.

Then

Even back then I hated duplication. Frankly, I hated doing validation at all; I damn sure hated doing it twice. Moreover, I hated server-side validation because doing it (without also doing a whole lot of extra work) involved:

  1. Submitting a form to an action page
  2. Having that action page validate the form values
  3. Displaying (what was usually) an ugly error message to the user
  4. Asking the user to use their Back button to return to the form
  5. Expecting the user to remember the errors that had been displayed
  6. Asking the user correct each error
  7. Rinse
  8. Repeat

That’s a lot of lousy user experience. Since that sucked so mightily, I chose client-side. With Javascript, I could validate user input without having to make the round trip to the server. Talk to me all you want about graceful degradation, but first of all, that concept didn’t exist in 1996 and second…this is Javascript we’re talking about.

Now

With the relatively recent proliferation of API‘s, server-side validation has taken on a whole new urgency. With multiple gateways into an application’s business core, it’s more critical than ever that validation be moved as far down the application stack as possible in order to eliminate redundancy and, in the process, ensure consistency. For most applications that I’ve built in the last 5 years or so, there’s been some kind of API involved.

With CakePHP

Once I had worked with CakePHP for a few months, I got comfortable enough to start thinking about validation and how I wanted it to work from end-to-end. I had a few requirements:

  1. I only wanted to write my rules and messages once. Do something once and you don’t have to worry about consistency. One less pain point is always going to be a good thing.
  2. I wanted to be able to validate without necessarily saving. Most of the time I’d be doing both, but decoupling them is just a good idea.
  3. Hide from the user the fact that my validation was happening on the server. To them, generating and displaying error messages should be a smooth and seamless experience.

I came up with something that seems reasonably elegant and has met my needs to date.

The Basics

Although I’m not a big fan of the semantics, CakePHP nicely decouples the validation and save actions. I would expect this of any framework, of course, but that’s a place to start. If I’m validating something independent of a save operation, I usually want that validation to return one or more error messages. CakePHP makes this easy and even offers me a convenient means of using my preferred semantics.

Never, Ever Mess With the Core

This is one of my basic tenets for developing with third-party libraries. Messing with core code is almost certain to destroy your upgrade path. Find another way.

In this case, I want to be able to validate model data based on the rules defined by that model and I want this validation to be available to all models in my application in exactly the same way. To do that, I created a copy of CakePHP’s app_model.php file as app/app_model.php. By creating a copy in my app/ directory, I can preserve my upgrade path, but still inherit from the proper parent class.

Validate

In the newly copied AppModel class, I added this function:

/**
 * function validate
 * 
 * Validates model data. This function can be called independently
 * on any model for validation independent of a save operation. It
 * can also be overridden, for example, by a model requiring more
 * complex validation.
 *
 * param   $data
 * return  array     An array of error messages or an empty array
 *                   if the data validates properly.
 */
public function validate ( $data = array() ) { $this->set ( $data );

/** * Corrected based on input from Miles Johnson in the comments * below. */ return !$this->validates() ? $this->validationErrors : array(); }

That snippet alone provides basic validation across my entire application with a simple line of code:

Model->validate ( $this->data )

Complex validation

Something of a misnomer since this isn’t very complex, but there are times when more is required. For example, if I have customers and vendors that have addresses, I usually want to break the address out so that the data structure can be shared. Separate table, separate model. The data is abstracted, but I can’t really have a valid customer or vendor unless their address also validates. To do that, I need to override my simple validate() method in the Vendor (or Customer) model so that I can do just a little bit more:

public function validate ( $data = array() ) {
   return array_merge ( $this->Address->validate ( $data ), parent::validate ( $data ) );
}

User Interface

As I alluded to initially, what I really want to do is to perform server-side validation while providing the illusion of client-side validation. To do that, I employ the appropriate controller. If I’m able to submit the entire form via Ajax, this becomes trivial because the same request can perform both actions (provided no errors are reported). Here’s how it might look on a vendor application page (/vendors/apply):

public function apply() {
   $errors = $this->Vendor->validate ( $this->data );

if ( !empty ( $errors ) ) { /** Package the errors for an ajax return */ echo json_encode ( array ( ‘errors’ => $errors ); ); exit(); } /** If there are no errors…save */ exit(); }

If I want to validate first (again, via Ajax) and then submit to a different action page, all I have to do is create a validate() method in the controller, make the Ajax call to that and, if no errors are returned, submit the form to the preferred action. If errors are returned, I can use jQuery’s JSON parser to extract the messages and drop them on the screen.

With this technique, I can consolidate my validation, rarely write more than one block of code to access that validation and present the results to a user attractively without that user ever being aware that anything more than client-side error handling has been done. What technique(s) do you employ to minimize your validation headaches?

Learning CakePHP: Foreign Key Constraint Violation

In the course of building forms with CakePHP’s form helper, I kept running into problems with the foreign key constraints in my database. I’d get an error if I didn’t enter a value in the form even though a NULL value was allowed at the database level. A little debugging indicated that the cause was CakePHP trying to insert an empty string into the foreign key field. Since there was no record in the parent table with an empty string id value, the database had no choice but to reject the insert request:

SQL Error: 1452: Cannot add or update a child row: a foreign key constraint fails (`mydatabase/events`, CONSTRAINT `events_ibfk_1` FOREIGN KEY (`location_id`) REFERENCES `locations` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE)

I will confess right now that I didn’t research this extensively and I don’t know whether this is something that’s easily avoidable using existing techniques or resources offered by CakePHP. It could also be that I’ve coded something incorrectly. What I saw, though, was an opportunity to learn. I saw an opportunity to create a behavior – something I hadn’t done yet – that would detect fields where a NULL value was acceptable and replace empty string values accordingly.

My project has events and events have locations. The relevant components of my database schema look like this:

CREATE TABLE locations (
   id            VARCHAR(255)   NOT NULL,
   name          VARCHAR(255)   NOT NULL,
   description   VARCHAR(255)   NULL,
   PRIMARY KEY ( id )
);
CREATE TABLE events (
   id            CHAR(36)       NOT NULL,
   location_id   VARCHAR(255)   NULL,
   name          VARCHAR(255)   NOT NULL,
   start_time    DATETIME       NOT NULL,
   end_time      DATETIME       NULL,
   PRIMARY KEY ( id ),
   FOREIGN KEY ( location_id )
      REFERENCES locations ( id )
         ON UPDATE CASCADE
         ON DELETE SET NULL
);

Using those tables are my Event and Location models:

class Event extends AppModel {
   public $name      = ‘Event’;
   public $belongsTo = array ( ‘Location’ );

/** content */ } class Location extends AppModel { public $name = ‘Location’; public $hasMany = ‘Event’; /** content */ }

In my event form, I have a dropdown list by which a user can select a location where the event will occur. Since not all events have a specific location, I’ve set the empty option to true so that the user can choose no association:

echo $form->input (
     'location_id',
     array (
        'div'      => 'input select',
        'selected' => $this->data['Location']['id'],
        'empty'    => true
     )
);

Unfortunately, my foreign key constraint didn’t like that empty option. My solution was to create a new Nullable behavior. All the behavior does is inspect a model’s schema and the data being saved. If a field is nullable and the data for that field is empty, the data values empty string is replaced with a null value. It does this in the beforeSave() callback function.

class NullableBehavior extends ModelBehavior {
   /**
    * function beforeSave
    * Looks for nullable fields in the schema and replaces empty string values for those fields
    * with NULL values.
    */
   function beforeSave ( $model ) {
      $schema = $model->schema();

foreach ( $schema as $field => $metadata ) { if ( $metadata[‘null’] ) { if ( isset ( $model->data[$model->name][$field] ) && $model->data[$model->name][$field] === ‘’ ) { $model->data[$model->name][$field] = null; } } } } }

Because I really like the technical accuracy that results from this solution even when constraint errors aren’t being thrown, I wanted to apply it to all of my models. Because I’m lazy, I didn’t want to write the code to add the behavior to all of those models. What I did instead was add it to my applications AppModel in the app/ directory.

class AppModel extends Model{
   public $actsAs = array ( 'Nullable' );
}

It solved my problem, but it may not be perfect or even necessary. Any feedback on my approach or the behavior itself would be appreciated.

CakePHP: Recursive Finds

(a.k.a It is not a boolean)

Being so new to CakePHP, I’ve spent a lot of time in the documentation. I mean a lot of time. Contrary to popular belief, the documentation is extensive, quite good and covers most, if not all, of the topics I’ve needed help with. One thing I’ve found, though, is that the documentation has three very specific deficiencies in many cases:

  1. Often, only simple cases are covered.
  2. There are a lot of ambiguities that I’d like to see clarified.
  3. It sometimes feels fragmented. The answer I’m looking for often isn’t found in (or even linked from) one place.

Today I ran into the latter two issues when I needed to pull data from an extended model association. I have an Attraction model that has a many-to-many (hasAndBelongsToMany) relationship with an Event model. Each event takes place at a location, so my Event model belongsTo my Location model.

When doing a find ( ‘all’ ) on my Attraction model, each result included, in addition to the Attraction itself, an Event – the model directly associated with with my attraction. I wanted to be able to display Location information as well, so I looked at the recursive parameter in an effort to have find() retrieve data from the extended association.

The find() documentation really provides very little information about the recursive parameter, but there is a syntax example:

'recursive' => 1, //int

When I looked at that, I read it to be a boolean value. To me, the parameter name itself sounds boolean and the only examples I found had a value of 1, a value commonly used (though not explicitly enough for my tastes) to represent a boolean “true”. When I set the parameter to 1, though, I got exactly the same result with the same lack of Location information.

As you may have guessed, I read it wrong. The recursive parameter is really a recursionLevels parameter. Once I set the value to 2, I got information related to the models that were directly associated with the Event model which included my Location information.

This is one of those cases where the documentation isn’t wrong, but it is fragmented and it’s not always easy for new developers to find what they need easily. As I get more familiar with CakePHP, more confident that I know what I’m doing and that I’m doing things the right (read: Cake) way, I’ll do my part to help clarify the documentation where I believe it necessary, but until then I’ll make my notes here in case they can help someone else or someone else can help me.

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.
← Earlier Posts Page 1 of 2