Drupal Tip: Never query CCK tables directly again

9 Jan

I'm currently in the midst of rebuilding Politicker from scratch with Drupal 6. It's a lot of work, but a good chance to get some things right that have always dogged us when maintaining the current (Drupal 5) iteration. One particular vow I've made is to avoid ever querying a CCK field or type storage table directly in the custom modules I'm writing. This is a common practice in most Drupal sites that I've seen and/or developed. It's a convenient shortcut, but it can cause problems.

If you have a content type “artice”, you might write a bunch of queries against the table content_type_article. Later, if you share one of the fields in this type to a new content type, your queries for that field will stop working, as the data will be moved to content_field_[fieldname]. For this and other reasons, I'd prefer to do this abstractedly rather than by hitting the tables directly.

Right now, CCK for Drupal 6 doesn't have a real query API. Fortunately, Views does, and CCK and Views are very tightly integrated, so it's possible to use Views as a query engine for CCK fields. The use case I'm dealing with now is reading an RSS feed and translating each item into a Drupal node, including filling some CCK fields with data from the feed. To avoid double-posting an RSS item, I need to compare the guid element against what I've stored in a CCK field.

In the past, I would have done this (assuming $rss_item is an object holding the RSS parse tree):

$nid = db_result(db_query("SELECT nid FROM {content_type_rss_article} WHERE field_guid_value='%s'", $rss_item->guid));
if ($nid) {
  $node = node_load($nid);
} 
else {
  $node = new stdClass;
  $node->type = 'rss_article';
  // fill out other node properties...
}

Instead, you can create a view and add an argument for “Content: field_guid_value”. In order to get actual answers from the view query, you'll also want to add “Node: Nid” as a field. With that done, it's almost trivial (thanks to the Views 2 API) to use this in your code to abstract away CCK queries. Let's say the view is named “guid_exists”:

$view = views_get_view('guid_exists');
$view->set_arguments(array($rss_item->guid));
$view->execute();
if (count($view->result)) {
  $node = node_load($view->result[0]->nid);
} 
else {
  $node = new stdClass;
  $node->type = 'rss_article';
  // etc...
}

Some things to note about this method.

  • The $view->result property is an array of objects, where each object has a property for each field you enable in the view. In this case I want the nid since I want to call node_load, so nid is what I asked the view for.
  • The “result” property is not really mentioned in the Views 2 API docs, nor is it listed in the public variables for the class. This may mean it's not guaranteed to be there in future releases, but if you look at the code it's pretty integral to how it works.
  • You don't need to worry about setting up display types in the view settings, since we're not concerned with the output of the view anyway.
  • I recommend exporting your view and implementing hook_views_default_views to define it in code rather than in the Drupal admin area. Makes sense to me since I'm using it in code.

Enjoy.