Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
I've been looking for a way to perform something like node_load()
, but with CCK fields as well and not just core node fields.
I've taken the original node_load()
and adapted it to be able to handle CCK fields as well.
I'm not caching nodes there according to nid, it's pointless. node_load()
does this just as well. I'm also not allowing to load nodes by nid only for the same reason.
Here's my suggestion code, please review it.
/**
* Based on the core node_load().
* Load a node object from the database, using both core node fields and CCK fields.
*
* @param $param
* An array of conditions for core node fields to match against in the database query.
* Since we also have the CCK conditions, this parameter can also be NULL.
* @param $cck_param
* An array of conditions for CCK fields to match against in the database query
* @param $revision
* Which numbered revision to load. Defaults to the current version.
*
* @return
* A fully-populated node object.
*/
function content_node_load($param = NULL, $cck_param = array(), $revision = NULL) {
$arguments = array();
if (is_array($param)) {
// Turn the conditions into a query.
foreach ($param as $key => $value) {
$cond[] = 'n.'. db_escape_table($key) ." = '%s'";
$arguments[] = $value;
}
$cond = implode(' AND ', $cond);
}
// Process CCK parameters.
$cck_tables = array();
if (count($cck_param)) {
// Turn the conditions into a query.
$i = 1;
foreach ($cck_param as $key => $value) {
$db_info = content_database_info(content_fields('field_'. $key));
$cck_tables[$db_info['table']] = 'cck'. $i;
$i++;
$cck_cond[] = $cck_tables[$db_info['table']] .'.'. db_escape_table($db_info['columns']['value']['column']) ." = '%s'";
$arguments[] = $value;
}
if (is_array($cck_cond)) {
$cond .= (isset($cond) ? ' AND ' : ''). implode(' AND ', $cck_cond);
foreach ($cck_tables as $table => $nick) {
$cck_join .= ' INNER JOIN {'. $table .'} '. $nick .' ON '. $nick .'.nid = n.nid';
}
}
}
// Retrieve a field list based on the site's schema.
$fields = drupal_schema_fields_sql('node', 'n');
$fields = array_merge($fields, drupal_schema_fields_sql('node_revisions', 'r'));
$fields = array_merge($fields, array('u.name', 'u.picture', 'u.data'));
// Remove fields not needed in the query: n.vid and r.nid are redundant,
// n.title is unnecessary because the node title comes from the
// node_revisions table. We'll keep r.vid, r.title, and n.nid.
$fields = array_diff($fields, array('n.vid', 'n.title', 'r.nid'));
$fields = implode(', ', $fields);
// Rename timestamp field for clarity.
$fields = str_replace('r.timestamp', 'r.timestamp AS revision_timestamp', $fields);
// Change name of revision uid so it doesn't conflict with n.uid.
$fields = str_replace('r.uid', 'r.uid AS revision_uid', $fields);
// Retrieve the node.
// No db_rewrite_sql is applied so as to get complete indexing for search.
if ($revision) {
array_unshift($arguments, $revision);
$node = db_fetch_object(db_query('SELECT '. $fields .' FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.nid = n.nid'. $cck_join .' AND r.vid = %d WHERE '. $cond, $arguments));
}
else {
$node = db_fetch_object(db_query('SELECT '. $fields .' FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.vid = n.vid'. $cck_join .' WHERE '. $cond, $arguments));
}
if ($node && $node->nid) {
// Call the node specific callback (if any) and piggy-back the
// results to the node or overwrite some values.
if ($extra = node_invoke($node, 'load')) {
foreach ($extra as $key => $value) {
$node->$key = $value;
}
}
if ($extra = node_invoke_nodeapi($node, 'load')) {
foreach ($extra as $key => $value) {
$node->$key = $value;
}
}
}
return $node;
}
Comments
Comment #1
alonpeer CreditAttribution: alonpeer commentedOh, and a way to use it would be something like:
Assuming we have a CCK field called "price" (which under CCK is translated to "field_price").
Comment #2
netbear CreditAttribution: netbear commentedInteresting, subscribe.
Comment #3
alonpeer CreditAttribution: alonpeer commentedComment #4
5t4rdu5t CreditAttribution: 5t4rdu5t commentedVery neat and most useful. Subscribe.
Comment #5
Anonymous (not verified) CreditAttribution: Anonymous commentedThis works as long as you don't have more than one cck field in the arguments.
But try this:
The problem is that as it finds each field it assumes the table alias can be incremented which assumes each field is in a separate table.
I'm looking at modifying this to work smarter with the table names but just wanted to share the issue.
Comment #6
KarenS CreditAttribution: KarenS commentedIt was brought to my attention that some people are using this patch so I want to clarify where it is likely going to go.
Doing what this patch is attempting to do is a far more complicated process than it may appear. The patch bypasses db_rewrite_sql and over-writes field values and then passes back a '$node'. Even though core does this for node_load it may not work as intended in this context, it would need extensive work and testing. If anything is wrong with the node it creates you could be allowing people to see data they shouldn't see. And if they do node_save() on the result, and it's not right, you could destroy data.
I certainly don't have time to test this and I would assume yched doesn't either. So unless yched disagrees, I'm marking this 'won't fix'.
Comment #7
yched CreditAttribution: yched commentedAgreed 100%
Comment #8
5t4rdu5t CreditAttribution: 5t4rdu5t commentedWell, this patch has proved extremely useful to me for writing custom migration code, so I'll share my fixes and and let others figure the pros and cons.
Here's the updated code to handle nodereference fields and multiple fields search from the same table:
Comment #9
aidanlis CreditAttribution: aidanlis commentedNeato, I'd love to have this in core. Subscribe.
Comment #10
matt.nz CreditAttribution: matt.nz commentedComment #11
mansspams CreditAttribution: mansspams commentedwhat is correct way to load node if cck field value is known? I need this for block visibility php code.
Comment #12
cpall CreditAttribution: cpall commentedI'm not sure exactly why, but his code didn't pick up my field right. So here's yet another version.
Comment #13
sja1 CreditAttribution: sja1 commentedYou can fetch a node (or nodes) within you php code based on the value of a cck field fairly simply using calls to views functions. The steps are:
This method I believe circumvents the potential problems raised by Karen in comment #6
Hope this helps somebody!
Comment #14
mheacock CreditAttribution: mheacock commentedIf all you plan to do is read node variables (no intention to save node), then this code is a perfect solution. (For those worried about the previous negative comments concerning untested code.) This code is perfectly safe and acceptable for code fetching purposes alone.
Comment #15
sammyman CreditAttribution: sammyman commentedThis is interesting. Could I use an argument with any of these codes to filter similar nodes based on a CCK field?
Right now I am trying to find a way to display similar content based on CCK through views. It would save me a lot of time if I could do so through views.
Comment #16
ayalon CreditAttribution: ayalon commentedVERY VERY usefull! Thanks a lot. Should be included in content module.
Comment #17
yongke CreditAttribution: yongke commentedHi don't know if this will be useful to anyone, but I have modified this code so that it load multiple nodes based on your criteria. I needed to do this for my application, maybe other people will too. I also cleaned up the code so that only the node's cck fields and nid are returned, making the results much more cleaner.
Comment #18
pvhee CreditAttribution: pvhee commentedSubscribing. This is indeed very much wanted for developers.
Comment #19
MamaGubs CreditAttribution: MamaGubs commentedIn order to load a node based on a cck field, I just did a db_query on the content type table using the applicable cck field, then did a node_load using the fetched nid. But I'm always worried about adhering to Drupal best practices - is the solution in this thread a more appropriate way to do this?
Thanks,
Mama Gubs
Comment #20
eotinfotech CreditAttribution: eotinfotech commentedsubscribing
Comment #21
agungsuyono CreditAttribution: agungsuyono commentedI don't know whether this is useful or not. I modified yongke's code so that it can handle userreference field. Also I change the return value becomes an array in case the result more than one node.
Comment #22
cannandev CreditAttribution: cannandev commentedsubscribing
Comment #23
jejk CreditAttribution: jejk commentedsubscribing
Comment #24
enboig CreditAttribution: enboig commentedBefore finding this post, I started writing my own function (which right now just support nodereference because is the only thing I needed despite node type).
I think it is great, I will grab most of this code, but I will change two things:
a) as #19 pointed; I will use this code just to find the nid, node_load(); will handle loading the node (after all it is its purpose and I doubt I could do it better than core).
b) I will just use one array() for all input fields. I don't think separate arrays are necessary, all cck fields start with "field_", I assume all the others are in {node}
By the other hand, I if this function gets tested, I would like to see it included in cck; any way to achieve this?
I will post my code as soon as I think it is ready.
Comment #25
enboig CreditAttribution: enboig commentedI finished my function, it returns an array of nids with any field (cck or not cck). You can call node_load() with the first element if you need it. I think this way it is more modular. It also uses db_rewrite_sql() so only return nodes where user can access.
Any comment is welcome:
Comment #26
leoklein CreditAttribution: leoklein commentedsubscribing
Comment #27
andredebruin CreditAttribution: andredebruin commentedVery elegant enboig.
I believe there is one bug: there where you have:
$res = db_query(db_rewrite_sql($sql), $param );
You probably meant:
$res = db_query( db_rewrite_sql($sql), $arguments );
Also, not completely sure why you create the return array the
way you do, I would think simply concatenating the results
like this:
$nids[] = $nid;
Is somewhat easier to use.
Comment #28
enboig CreditAttribution: enboig commentedI think "$res = db_query(db_rewrite_sql($sql), $param );" is the right way, $param is intended for db_query(), not db_rewrite_sql().
About "$nids[$nid] = $nid;" it is to avoid duplicated nids. I know a "SELECT DISTINCT" should do the job, feel free to test both solutions to check performance.
Comment #29
andredebruin CreditAttribution: andredebruin commentedOops, my bad. I do indeed use db_query() in my version.
Comment #30
manuelBS CreditAttribution: manuelBS commentedI prefer usinge #21 but I woul suggest to use the vid in the JOIN - ON Statement,otherwise you will get douplicate nodes (all revisions) of the node what would not be revision save.
And I would like to choose if I get completely loaded nodes or only nids.
So my Version is:
Comment #31
ice5nake CreditAttribution: ice5nake commentedI'd love to see this be a part of CCK. In the ORM world this is a standard feature. This module got started but it doesn't seem like it matured very far, http://drupal.org/project/orm
Judging from the interest in this thread this feature could help enhance Drupal as an application development platform. This is in fact at least the second time I've gone looking for this functionality
Comment #32
LeDucDuBleuet CreditAttribution: LeDucDuBleuet commentedThx to all, this function helped me a great deal even today! :-)
I took the revision in #30 and it worked like a charm for nodereference fields!
Comment #33
arvindkumargupta CreditAttribution: arvindkumargupta commentedsubscribing