Reading node field values in module code using Drupal 6 - php

I have created a custom module and am using hook_block to programmatically create some blocks.
My question is how can I access field values including CCK fields for the current node within my module?
I basically want to get a value from a CCK field and use the value when building my block for that page.

Getting at the current node is an awkward pain in the posterior. Standard practice is to do something like this:
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == '') {
$node = node_load(arg(1));
// Collect output.
}
arg() pulls elements out of the Drupal path. Since all nodes (regardless of what a path alias might show you) appears at node/#, by checking for 'node' and that the second element is a number, you are fairly well guaranteed to have your hands on a node. Checking the third path element allows you to avoid processing on the node edit form and other pages that hang off a specific node.
CCK Values are loaded into the node, and usually look something like this:
// Text field. Structure also works for number fields.
$text = $node->field_my_text_field[0]['value']
// Node Reference field.
$nref = $node->field_my_nref_field[0]['nid']
// User Reference field.
$uref = $node->field_my_uref_field[0]['uid']
The "0" array element specifies the delta of the field. Any given field can actually process multiple values, and the array structure in CCK assumes this possibility even if you restrict the field to a single value.

In Drupal 6 there is a built-in Drupal function to get the node object.
if ($node = menu_get_object()) {
…
}
Read more here http://api.drupal.org/api/function/menu_get_item/6.

Related

PHP check if XML node exists before saving to variable?

Here is a snippet of the xml I am working with:
My example xml
A client requested that we add the ability to filter which type of "news articles" are displayed on specific pages. They create these articles on another website, where they now have the ability to assign a one or more categories to each of the articles. We load the articles via php and xml.
The error I receive is:
Call to a member function getElementsByTagName() on null in ...
Here is the code from 2012 that I am working with:
$item = $dom_object->getElementsByTagName("Releases");
foreach( $item as $value )
{
$Release = $value->getElementsByTagName("Release");
foreach($Release as $ind_Release){
$Title = $ind_Release->getElementsByTagName("Title");
$PublishDateUtc = $ind_Release->getAttribute('PublishDateUtc');
$DetailUrl = $ind_Release->getAttribute('DetailUrl');
$parts = explode('/', $DetailUrl);
$last = end($parts);
I am trying to transverse to the category code and set a variable with:
$newsCategory = $ind_Release->getElementsByTagName("Categories")->item(0)->getElementsByTagName("Category")->item(0)->getElementsByTagName("Code")->item(0)->nodeValue;
This loads the current 2018 articles with the category slug being echoed, because they have an assigned category, but it fails to load 2017, 2016, and so on, I believe, because they are not assigned a category within the XML and this is breaking something.
A news article without a category appears with an empty categories node within XML
I understand that I am using getElementsByTagName, and because there is no element beyond the first categories node it breaks.
Is there a way to check that there is indeed a path to Categories->Category->Code[CDATA] before trying to set it as a variable and breaking it?
I apologize if this is confusing, I am not a PHP expert and could use all the help I can get. Is there a better way to transverse to the needed node?
Thanks.
You need to use XPath. If you're using DOMDocument, this is done via DOMXpath.
Your current approach uses chaining, and the problem with chaining is it breaks down if a particular juncture of it doesn't return what the following method relies on. Hence your error.
Instead, check the whole path from the start:
$domxp = new DOMXpath($dom_object);
$node = $domxp->query('/Categories[1]/Category[1]/Code[1]');
if (count($node)) {
//found - do something
}

Drupal 7: How do I retrieve a node's Content Type from within a component's prepocess function?

I've got a component that requires me to get the content type of the node the component appears on. I saw there exists a function to get that for me:
node_type_get_name($node)
However, when I use this function within the components pre-process hook, eg:
my_component_preprocess_my_component_theme(&$variables, $node) {
dpm(node_type_get_name($node));
}
I get nothing.
If I dpm($node); I get the title of the component, not the node.
Would anyone know how I can retrieve the node's content-type?
You can load current node like this:
<?php
$node = menu_get_object();
if ( !empty($node) ) {
print "Have node";
}
?>
So, you'll have full node loaded and can check it's type ($node->type) and anything else you need.
https://www.drupal.org/forum/support/post-installation/2010-07-28/how-to-get-node-object-or-nid-for-the-current-page
And if you don't have the node it means, of course, current page is not a node (maybe a view or some custom page...).

Difference between hook_node_type() and hook_nodeapi()

I'm trying to understand the difference between hook_node_type and hook_nodeapi
I've tried to use hook_node_type with the story node and a simple dsm but got nothing.
function mymod_node_story($op, $info){
dsm($info);
}
hook_node_type is made to hook into when a node type is changed. Fx if you want to allow comments on node type story etc.
hook_nodeapi is used to be able to hook into the different processes that can happen to an actual node, like when a node is saved, when it's being viewed etc.
There's not a hook for specific nodes, but instead you usually do something like this.
function module_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
if ($node->type == 'story') {
switch ($op) {
case 'load':
...
}
}
}
hook_node_type() is invoked each time a content type is deleted, or renamed. The purpose of the hook is to rename the Drupal variables used for the content type (which is automatically done for any form field included in the content type edit form, from node_type_form_submit()), or to delete the Drupal variables containing values for the content type that has been deleted.
hook_nodeapi() is invoked for each node when it's loaded from the database, the node edit form is shown, the node is visualized, when the node is being indexed from the search module, when a RSS feed, or the node is saved. A module should implement hook_nodeapi() to alter nodes for content types implemented by other modules.

Drupal - How to update a CCK NodeReference field programmatically?

I'm trying to create a node (B type) & assign it to a A type node's CCK nodereference field using node_save() method.
$node_type_A = node_load($some_nid);
$node_type_A->field_type_B_node_ref[]['nid'] = $node_type_B_nid;
$node_type_A = node_submit($node_type_A);
node_save($node_type_A);
As the result, a new B type node will be created, but no reference will be assigned to the A type node. any help would be appreciated.
GApple is right, the format is correct, but there are couple of things that you might want to care about.
Delta Value
First you need to know the delta value of the latest node reference attached to $node_type_A, the delta is actually a partial index, when combined with vid field of the $node_type_A, they become the index for node reference table in the database. In other words, its a count for $node_type_B which are referenced in $node_type_A, ok?
GApple is right again, you have to exactly say where to add the new reference. When you got that delta value you can exactly say where to append (delta+1) the new reference. Here it is:
function get_current_delta($node_vid){
return db_result(db_query("SELECT delta FROM {content_field_type_A_node_ref}
WHERE vid = '%d'
ORDER BY delta DESC
LIMIT 1", $node_vid));
}
Adding the new reference
We got delta! so we can attach the new $node_type_B node to our $node_type_A node:
// Loading type_A node.
$node_type_A = node_load($some_nid);
// Getting current delta value.
$current_delta = get_current_delta($node_type_A->vid);
// "Appending" a node reference based on delta.
$node_type_A->field_type_B_node_ref += array($current_delta + 1 => array('nid' => $node_type_B_nid));
Resaving the updated node
Optionally call node_submit() to populate some essential fields in the node object and save it by utilizing node_save(). After all, you need to call content_insert() to make the node completely saved asidelong with its CCK fields:
// Resaving the updated node.
$node_type_A = node_submit($node_type_A);
node_save($node_type_A);
content_insert($node_type_A);
Flushing the content cache
Probably the most important part, this was killin' me for couple of days. CCK has a cache table in the database called cache_content (take a look at its structure), after resaving the updated node, you will notice that nothing has changed in the $node_type_A theme output even though that the tables are updated. We have to remove a record from that content cache table, this will force Drupal to show the latest snapshot of the data. You can define the following as a function:
db_query("DELETE FROM {cache_content} WHERE cid = '%s'", 'content:' . $node_type_A->nid . ':' . $node_type_A->vid);
Hope it helps ;)
I just checked one of my own modules that does something similar for the object format, and $node_type_A->field_type_B_node_ref[]['nid'] should be correct.
One thing to check for is that when you load the node, CCK may pre-populate the node reference array with an empty value. If you have configured the field to only allow one value, by using the array append operator (field_type_B_node_ref[]) it will create a second entry that will be ignored (field_type_B_node_ref[1]), instead of overwriting the existing value (field_type_B_node_ref[0]). Try explicitly specifying the array key if possible.
Great post, but one correction: don't flush cache entries by manually querying the DB. In the event someone is using memcache or any other external cache it's going to fail.
cache_clear_all() is your friend for clearing.
Suggested code, direct from the CCK module:
cache_clear_all('content:'. $node_type_A->nid .':'. $node_type_A->vid, content_cache_tablename());
I show CCK storing node references as $node->field_node_reference[0]['items'][0]['nid'], not $node->field_node_reference[0]['nid']. Have you tried mimicking that?
"Flushing the content cache" This works for me, especially if you get a data from node_load()

Drupal: replacing field value in View

I have Drupal 5 View (must be "Views 1" so), which contains Event_StartTime field, which shows up normally for the fields that have a real date inside. But I also have a number of records with Event_StartTime field value like Dec 31 1969 - 8:00pm which looks bad in view and I need to replace them with some custom label, like "No Date Available".
Can I somehow inject this condition (if value = x show value y) into view?
Even when I print in "No Date Available" into SQL, the view will show empty field.
To sum up, the general problem is that View fields are kind of limiting, and sometimes you just need to replace some field value, but it's either "date or nothing" or "node-reference id or nothing".
P.S. The situation might be a bit complicated because I use "Bonus: Views Export" module which makes my view return CSV data.
I'm not really familiar with Views1 anymore, but I think you have at least two options:
At the theming level - you could override the field template and put a check for the particular date value in there, replacing it when found. Of course this check would depend on the formatting of the dates, so if you ever change that, you'd need to adjust the check as well.
At the view generation level - you could execute the view 'manually' from code and inspect the views object right after instantiation and/or after execution, but before rendering (See her for an example of manipulationg the view object in Views2). You could manipulate the result property after execution, but before rendering, or you might be able to put a pre-render callback in there right after instantiation (but I'm not sure if this is already possible in Views1).
Building off of Henrik's comment, theming seems to me to be the best place to do this. You would need to add a function to your template.php file to override that specific view. See the Views 1 theming documentation. Based on this documentation, you create a function with the name of your view of the form
function THEMENAME_view_view_VIEWNAME($view, $type, $nodes, $level = NULL, $args = NULL)
to theme the whole view. $nodes is an array of partially-loaded node objects that are included in your view. You can load each node and check the values of the Event_StartTime field with something like this:
foreach ($nodes as $id => $node) {
$temp_node = node_load($node->nid);
//now check value of Event_StartTime assuming that is what you named the field
if ($temp_node->Event_StartTime === 'Dec 31 1969 - 8:00pm') {
$temp_node->Event_StartTime = 'No Date Available';
}
//output the fields of your view . . .
}
I only showed how to check the value of the Event\_StartTime field, and I am not sure what the actual value is. Dec 31 1969 is before the dates returned by the time() function, so if this does not work you might want to check if Event_StartTime === '', which may be true if the event field is just left empty. You will still have to theme the rest of your fields and output them as HTML, which might be too much of a pain just to change the output of one field. You can also override the output of individual fields. I have not done this so I couldn't walk you through it, but the link I gave above has some documentation on overriding field theming functions.

Categories