Drupal Tip: Never query CCK tables directly again

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.

Comments

One of several Coolest

One of several Coolest Fashion accessories - Swiss Replica Wrist watches

HI

hello,my friend

Default display?

Does this only pull from the "Default" display in the view? Is there a way to call other displays in the View?

drupal.org article?

This looks great! I'll be using this in my module shortly. In fact, I think that this would be really nice to contribute to the Views 2 documentation.

Thanks for sharing! :D

Use view->preview()

Hi,

thanks for this tip. There only is one thing you might want to change: use
$view->preview();
instead of
$view->execute();

execute does not take into account certain settings in the view, especially "Items to display". See this post on ddo: ...run the pre_execute phase manually.

Thanks Matt

Thanks Matt for this.

I've been trying to find out how to access CCK fields programmatically.

So views would be the way to go, you make it obvious.

cheers

Cool approach.

Hi,

I really like how you're doing this, but how many queries does Views module on each call? Isn't that counter effective? Just curious.

Performance?

I am now moving to Drupal6, and climbing the whole Views2/CCK2 learning curve, so this post was very helpful. I think that your strategy is quite valid - I had run into CCK fields be un-reliably placed in separate tables, and this seems the right way to do it. Excellent point about exporting the view and putting it in code. I worry in general about portability of things you create via the GUI.

I am interested in the overhead of this procedure. I am just trying to get a sorted list of nodes based on a CCK weight field that the content_type contains.

Depends on your traffic

In general, I find Views 2 to be quite efficient. It generates very succinct SQL. The main performance hit in most DB operations will be JOIN'ing a lot of tables, so probably whatever it generates would be as efficient as what you'd write by hand. With the regards to the PHP CPU or memory overhead, I've never found that to be a problem. The sites I manage are all behind an edge cache or memcache (or both), so this type of overhead is usually not a major concern anyway. A PHP opcode cache could also help out in this type of situation.

I have used this tutorial for

I have used this tutorial for my website and it works perfect.
Thanks alot for sharing this.

Nice post Matt, So I'm a new

Nice post Matt,

So I'm a new hand at drupal and everyone seems to be all about CCK, but I have been worried about doing anything fancy with it not really knowing what I would mess up.

For your great work I'll do the usual thing and ask you for more work. I have been trying to find out what the "proper way" to added CCK content through code is. Do you just insert it right into the data base your self?

Like when I make a new piece of type A content I either want to update a piece of content type B (querying with what you just said above thanks!), or make a new type B (A to B is a one to many relation).

Thanks,
Tristan

p.s. I'm currently a want to be astronomer that is also start to thing about Jumping ship :)

I would do it programmatically

It's usually okay to update existing CCK tables, but as I've discussed here, I'm sort of coming to believe that you should not do that and let the internal DB functions handle it all for you -- in the future, that's how our code will continue to be maintainable. I would recommend using node_load() to get the existing node and then running it through print_r() to see its structure. Basically you just need to create the structure of the CCK fields (called field_[something]) and then pass the object to node_save(). CCK takes care of the rest.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
11 + 0 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
By submitting this form, you accept the Mollom privacy policy.