Adventures in Drupal 6

As soon as we get the Ubercart 1.0 release out the door, we're going to dive into converting the entire project to Drupal 6. Drupal 5 has served us well for over a year now, but many features offered by the major API changes/additions (menu, forms, schema) are just too sweet to miss out on when we start making our own Ubercart sites. I know we'll be taking a beating for at least a couple weeks updating all our modules, but ... well, no "but" - it's just going to hurt. ;)

In anticipation of the update, I've decided to dive into module development in Drupal 6 early. As my pilot project, I've started building out a budget module my wife and I can use to monitor our spending through a Drupal site. I'm hoping other people and organizations will find online budget tracking handy, and I'm betting they'll be happy to use Drupal 6 to do it. (How sweet will it be to be able to enter receipts remotely? Your budget won't get torn to shreds when you go on vacation.) After several hours reading d.o, chatting in IRC, and trying everything until my eyes hurt, I'm starting to feel comfortable with the changes.
<!--break-->
So, Drupal 5 module developer... what can you expect when you come to Drupal 6? A lot of new, and a bit of confusing. The documentation is still being fleshed out and arranged, so you should be gracious to those posting it up as you try to find that bit of info you need. Overall, it's been pretty helpful, and there are always plenty of examples in Drupal core for just about anything you'll need to do. You can expect to spend a few hours grokking the changes to the menu and forms APIs, even if you were there to see Jeff's ewoks dance in Barcelona. There's just too much going on for folks familiar with the old systems to grasp all the changes in one go. Also, you can expect to spend plenty of time learning to use the new schema API. Here again, I just opened up as many .install files as I could find in core and went to town trying and retrying my own. Just keep reminding yourself that this really is a huge improvement over the old method (or lack thereof) of creating tables!

A few things to look out for that I wasn't expecting and don't know if they're documented:
<ul>
<li>It seems truncating cache_menu no longer does the trick when fooling with your menu hooks to get them right. I've kept the modules page open and disabled/re-enabled the module while working on it. Surely someone can point me to a higher path. 8)</li>
<li>The sequences table is out and auto_increment (and other DB equivalents) are back in. The schema API lets you specify a column's type as 'serial' and takes care of the rest. I got snagged here on a default value in my schema (was copy/pasting...). As far as I know, you should not specify a default value on a serial column. (At least setting it to 0 is going to give you a nice error when it tries to install.)</li>
<li>All form builder functions are being passed an argument... the catch all <b>$form_state</b> array. Either I missed this in the update notes or it wasn't yet in the right place, but this had me baffled for a bit till I realized that the first parameter passed to your function when you use <b>drupal_get_form()</b> is always going to be <b>$form_state</b>. You should account for this if you're counting on a form receiving any arguments, like this one from my <em>budget.module</em>:
<?php
function budget_category_form($form_state, $category = NULL) {
// ...
}
?></li>
</ul>

I also hopped on IRC briefly with my fingers crossed hoping to find someone knowledgeable of the new menu API. I half-heartedly asked if anyone around could help when lo and behold, the Architect himself, chx, waved at me. I leapt for joy (inside, not literally) in my chair and got to chatting. You see, I needed some local tasks to show up only on the edit form of budget nodes, but I was having a hard time restricting them to nodes of that type. I had read in the docs about <a href="http://drupal.org/node/109153">dynamic argument replacement</a> and thought I might be able to do something with that, and chx showed me how. Joy. :D

So, the skinny is... menu item paths now accommodate some wildcard action using the handy %. In my menu items, I was able to put in %node_budget with a coordinated load function that checks if a node having an nid equal to the value of %node_budget exists and is of the budget type. If that function returns FALSE (bad nid or not a budget node), then the menu path is a 404 and won't be displayed in the local tasks area of whatever node you're editing. This may sound vague, but check the code and the screenshot. You'll know if you'll never need this information and can go ahead and forget you ever read this paragraph. For everyone else, as soon as I look at the code making this work, I'll be posting this to the <a href="http://drupal.org/node/102338">menu system docs</a> per chx's request. Thanks again, chx!

<?php
// In budget_menu:
$items['node/%node_budget/edit/budget'] = array(
'title' => t('Edit budget'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'access callback' => 'node_access',
'access arguments' => array('update', 1),
'weight' => 0,
);
$items['node/%node_budget/edit/categories'] = array(
'title' => t('Edit categories'),
'page callback' => 'budget_categories',
'access callback' => 'node_access',
'access arguments' => array('update', 1),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
'file' => 'budget.admin.inc',
);

// The function called when a path that may resolve for either of those items is loaded:
function node_budget_load($arg) {
return (is_numeric($arg) && ($node = node_load($arg)) && $node->type == 'budget') ? $node : FALSE;
}
?>

So, on a page node, those menu items won't show up in the local tasks area at all. However, on a budget node, you'll see them both and get to further setup your budget node. How cool is that!? 8) See the screenshot below for a visual. Also, unless I'm way off, <b>node_load()</b> caches the node so you needn't worry about superfluous database queries.

So... these are the start of my adventures in Drupal 6. Won't you come adventure with me?

Topics: 

Comments

In Drupal 6.x latest, module page displays (reloads) themselfs clear the cache due to otherwise appearing bugs in the order of how things are rebuilt and modified in the database. So a refresh on the modules page will do the cache refresh.

Excellent news! I'll update this evening. Thanks for the heads up. Smile

The first "problem" you describe - the clearing of the cache_menu table - can be done like this: add a call to menu_rebuild() in your index.php. pwolanin pointed me to this some time ago, when I was testing Drupal 6 patches that affected menus.

That magical coordinated load function seems very handy to me. Thanks for sharing! Smile

Just reload the module page. It now rebuilds the module tables and theme registries on every load. No need to do anything, just reload.

Great writeup and I so look forward to your budget module!

Couple things...

1) node_load does cache the nodes it loads.

2) There is a function in the devel module to clear the menu cache. Though, I'm sure there is a good way to do it without devel module I just don't know it.

Actually, as of this week there's now a way to do it in Drupal core -- the admin/settings/performance page has a big CLEAR CACHED DATA button that will rebuild menus, clear cache tables, reset the CSS and JS caches, and rebuild the theme registry in one step. Smile

Thanks for the report, Jeff. The replies to this post just get better and better.

*cue the dancing ewoks*

I'd completely forgotten about the Ewoks. Thank goodness for this post.

Good work as always Ryan Wink

Check out Xero.com. My dream accounting and budgeting app is to have something like Xero running on drupal. Xero has got everything right, except that they run on a proprietary platform (.Net).

This is a glimpse of Xero; http://drupal.geek.nz/node/50

"$form_state" is SO not a word.