Displaying similar nodes - drupal

I've somewhat ran in to a problem with Drupal today.
I would like to display a node (Product) on a page, and below that node, I'd like to display 3 similar nodes (Products). Similar being: having the same taxonomy id or having a "promoted" tag attached to it.
I've tried crafting the related nodes into a view which is being displayed as a block, only when we're on a product's page. I didn't get far with this.
My second thought was making a panel page with 2 views on it, one for the product, and one for the related products. I also didn't get far with this.
Does anybody know the easiest way to accomplish this?
Update:
I have tried both answers, I am not receiving any related products though. The SQL query that's executed (term id = 1) is:
SELECT node.type AS node_type, node.title AS node_title, node.nid AS nid, node.created AS node_created FROM {node} node INNER JOIN {taxonomy_index} taxonomy_index_value_0 ON node.nid = taxonomy_index_value_0.nid AND taxonomy_index_value_0.tid = :views_join_condition_0 WHERE (( (node.type IN ('product')) AND (taxonomy_index_value_0.tid AND '') AND( (taxonomy_index_value_0.tid IN ('1')) ))) ORDER BY node_created DESC LIMIT 10 OFFSET 0
When I manually execute the query and remove AND (taxonomy_index_value_0.tid AND '') from the query I do receive the related products.
Does anybody know what causes the code to be added to the query and how to fix it?
Update 2:
I've removed the "Allow Multiple Terms per Argument" and am now getting the related products. I don't know what this means for my site though.
Update 3:
I am using Drupal 7 by the way.

Override your node view with panels. And create a view block with 'taxonomy id argument', you need to choose default argument options as PHP Code and place this code.
$node = node_load(arg(1));
if($node) {
foreach($node->taxonomy as $term) {
$term = $term->tid;
return $term;
}
}
I just launched a site using panels + views magic. http://sgigulf.org/culture/synopsis-of-performers-showcased-by-sgi-gulf

Take a look at the RelatedContent module. Links to the module and a couple of tutorials below:
http://drupal.org/project/relatedcontent
http://drupaleasy.com/blogs/ryanprice/2008/06/using-views-2-drupal-6-create-a-related-pages-block
http://www.hankpalan.com/blog/drupal/related-content-views-2-drupal
You say you're having trouble with the display. In that casea, make the view from the above instructions a block, and have it display in a region that's below the node content, though that assumes there's a region in your theme directly below your content.

You can have multiple terms when you change to
$node = node_load(arg(1));
if ($node) {
$ret = array();
foreach ($node->taxonomy as $term) {
$ret[] = $term->tid;
}
return implode('+', $ret);
}
return '';
The '+' in implode is OR. If you want AND, than use ',' instead

Related

Displaying field collections loop in Content Type TPL file for drupal 7

I have a content type "about" created in Drupal 7. I have a field collection named "field_usf_projects" which is set to unlimited and contains 2 fields, "usf_title" and "usf_description". Now I want to run a for loop which retrieves the field_usf_projects and then displays 2 fields namely ("usf_title" and "usf_description") inside a ul - li structure.
I have gone through many links but cannot find a working solution. Please help me with this.
Thanks in advance.
Here is my solution, on hook_node_view you can use the entity wrapper to get the fields
function mymodule_node_view($node, $view_mode, $langcode) {
// Check if the node is type 'about'.
if ($node->type != 'about') {
return;
}
// Get the contents of the node using entity wrapper.
$node_wrapper = entity_metadata_wrapper('node', $node);
// Get the contents of the field collection.
$values = $node_wrapper->field_usf_projects;
// Loop field_usf_projects.
foreach ($values as $item) {
// Print the values of the fields.
var_dump($item->usf_title->value());
var_dump($item->usf_description->value());
}
}
Instead of dumping, you can add the markup for your
A nicer thing to do would be use the hook_preprocess_node to add the markup straight into the $variables, and print them via template.
https://api.drupal.org/api/drupal/modules!node!node.module/function/template_preprocess_node/7
I can tell you how I'm handling this, event it's a bit dirty and I'm risking to be crucified, but it works for me. If you are inside node template you have $node object. Print it with print_r or similar way and just follow the structure of output to get to your data. It will probably be something like:
$node->field_usf_title['und']...
If you don't have that $node object find node id and load the node with
$node = node_load($nid);
first.
I was finally fed up with Field collection. I cannot get any data. I have used Multifield which is way too much better than Field collection. Please see it at https://www.drupal.org/project/multifield
Let me know what is better multi field or Field Collection.
Thanks!

How to make paged navigation in products more info page

I use Drupal 7 with Commerce module and making products more info page with links to prev/next products and others pages in product category.
http://postimg.org/image/8rbcsyye3/
Products preview/announce located at category page.
It's easy to do as views page with pager and number item per page equal to 1. But how can I jump from product preview page to product more info correctly? In that case it's need to navigate to specific page number in pager. How it's possible? or may be exist more appropriate way to solve it's task.
In Pager API I found the only function pager_find_page it's not even clue to pager_set_page. Maybe because it is described somewhere somehow, but I can't find it any way.
Answer to own question.
Solution for Drupal 5/6 (not sure about version)
Some other helpfull information
https://drupal.stackexchange.com/questions/1561/is-it-possible-to-dynamically-set-views-pager-settings
https://drupal.stackexchange.com/questions/16046/alter-pagination-start-index
After mixing that information I wrote simple solution for Drupal 7.
When user going to product node he can jump to any other product in current category.
That's why I create view with ajax enabled and contextual filter which return all node's ids from same product category by passing current page node id.
$n_id = intval(arg(1));
$nnm = db_query("select t.nid from {taxonomy_index} t left join {node} n on n.nid = t.nid where t.tid = (select t2.tid from {taxonomy_index} t2 where t2.nid = :vid) order by n.title", array(':vid' => $n_id));
$res= array();
if ($nnm->rowCount()) {
foreach ($nnm as $row) $res[]= $row->nid;
$handler->argument= implode('+', $res);
return true;
} else {
return false;
}
At custom module for my products full information view I added hook_views_query_alter.
Only in query_alter hook $view->query->where parameters are available and $view->query-pager not defined yet. It's mean that no need to rewrite default value for pager->current_page and product category's nids are avalable.
function [modulename]_views_query_alter(&$view) {
//$_GET['wtf'] -- variable to check first view load
if ($view->name == "view_name" && $view->current_display == 'view_display' && !isset($_GET['wtf'])) {
//Search current node id ($view->args[0]) in all category's nodes
$res= array_search($view->args[0], $view->query->where[0]['conditions'][0]['value'][':node_nid']);
if ($res) {
//$view->set_current_page($res);
//Set page id for pager
$_GET['page']= $res;
}
$_GET['wtf']= 2;
}
}
It is strange that there is no mention of such way to provide pager->current_page value.
Unlike pager->set_current_page method it can set page value at any views hook even if $view->pager not been initialized. It sounds pretty native for me.
And thank you for correcting my mistakes, I'm sorry for my bad english.

Drupal 7 node_save not saving computed fields during cron

I have a Drupal content type which contains a number of computed fields. Some (but not all) items are being added to this content type via a cron-triggered RSS feed importer. I'm trying to trigger computed field generation for new items in hook_cron. The following code grabs all items that haven't been tagged as 'submitted', loads and re-saves the node, and then marks the node as 'submitted'.
$query = db_select('node', 'n');
$query->fields('n', array('nid'));
$table_alias = $query->join('field_data_field_submitted', 'r', 'n.nid = r.entity_id AND r.field_submitted_value = 0');
$result = $query->execute();
foreach ($result as $record){
$q = $record->nid;
$n = node_load($q);
node_save($n);
$query = db_update('field_data_field_submitted')
->fields(array('field_submitted_value' => 1))
->condition('entity_id', $q)
->execute();
}
This code works the way I expect it to if I call it from a module-generated page (created using hook_menu with a page callback function). Nodes are resubmitted, and the computed field data is generated. When I put this code in my hook_cron function, the query works, it loops through the records and updates the 'submitted' value, but the computed fields are not computed. I'm confused as to why this would not get triggered in cron. Any help?
Doh! Finally realized that this was completely my own doing. Due to the nature of this content type, where we allow anonymous users to create new content, but explicitly do not trigger the computed fields when they create the content (long story, but short form is that authenticated users then verify & enhance this content, which is where the computed fields come in). So, as I was setting up the initial content, I disabled the computed fields for anonymous users (if $user->uid > 0), and completely forgot about that. Once I tweaked that logic to allow computed fields to be processed on import (triggering it with a field that has a value for the imported content, but not for other content), the problem was solved.
The cron run has access to the full bootstrap so there's no logical reason why your code would produce different results in that context.
That said, you're only updating the field_data_field_submitted table when you also need to update the field_revision_field_submitted table, so that might somehow account for the discrepancy.
Drupal provides an API for the field system so that these sorts of problems can be avoided completely. The same code you've used, rewritten the 'Drupal' way, would be:
$query = new EntityFieldQuery;
$query->entityCondition('entity_type', 'node')
->fieldCondition('field_submitted', 'value', 0);
$results = $query->execute();
if (!empty($results['node'])) {
$nodes = node_load_multiple(array_keys($results['node']));
foreach ($nodes as $node) {
$node->field_submitted[$node->language][0]['value'] = 1;
node_save($node);
}
}
I can't think of a good reason why the above code would fail on cron either so it might be worth giving it a whirl.

Theme CCK fieldset

I am attempting to use the CCK theme_fieldgroup_fieldset($elements) hook to convert the fieldset to a two column layout.
I thought that this shouldn't be to hard because the individual fields are in the $elements variable so all I have to do is iterate over them and print them individually. the problem is that I have no way to tell if they have been excluded from display on the "Display Fields" tab of the content type.
Does anyone have any ideas for me? Am I trying to do this the hard way or what am I missing?
Following is the solution that I came up with. The biggest problem is that it requires a database query for every field. These isn't the greatest, but it works so what can you say?
function _brioratheme_include_cck($field) {
$query = "SELECT display_settings AS ds FROM {content_node_field_instance} WHERE field_name = '%s' LIMIT 1";
$result = db_query($query, $field);
if ($result) {
$row = db_fetch_object($result);
$display_settings = unserialize($row->ds);
return !$display_settings['full']['exclude'];
}
}

Removing [nid:n] in nodereference autocomplete

Using the autocomplete field for a cck nodereference always displays the node id as a cryptic bracketed extension:
Page Title [nid:23]
I understand that this ensures that selections are unique in case nodes have the same title, but obviously this is a nasty thing to expose to the user.
Has anyone had any success in removing these brackets, or adding a different unique identifier?
Ultimately, you need to change the output of nodereference_autocomplete() in nodereference.module.
To do this properly, you want a custom module to cleanly override the function.
This function is defined as a menu callback, thus,
/**
* Implementation of hook_menu_alter().
*/
function custom_module_menu_alter(&$items) {
$items['nodereference/autocomplete']['page callback'] = 'custom_module_new_nodereference_autocomplete';
}
Then, copy the nodereference_autocomplete function into your custom module, changing it's name to match your callback. Then change this one line:
$matches[$row['title'] ." [nid:$id]"] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
Dropping the nid reference.
$matches[$row['title']] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
I believe the identifier is purely cosmetic at this point, which means you could also change the text however you like. If it is not purely cosmetic, well, I haven't tested to see what will happen in the wrong conditions.
I always meant to identify how to do this. Thank you for motivating me with your question.
What Grayside has posted will work... as long as you don't have two nodes with the same title. In other words, if you want to do as Grayside has proposed, you need to be aware that the nid is not entirely unimportant. The nodereference_autocomplete_validate() function does two things. It checks to see if there is a node that matches, and if so, it passes the nid on, setting it to the $form_state array. If it can't find a node, it will set an error. If the nid is present, it will be used to get the node, which also is faster, the code is here:
preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $value, $matches);
if (!empty($matches)) {
// Explicit [nid:n].
list(, $title, $nid) = $matches;
if (!empty($title) && ($n = node_load($nid)) && $title != $n->title) {
form_error($element[$field_key], t('%name: title mismatch. Please check your selection.', array('%name' => t($field['widget']['label']))));
}
}
This just checks to see if there is a nid and checks if that node matches with the title, if so the nid is passed on.
The 2nd option is a bit slower, but it is here errors can happen. If you follow the execution, you will see, that if will try to find a node based on title alone, and will take the first node that matches. The result of this, is that if you have two nodes with the same title, one of them will always be used. This might not be a problem for you, but the thing is, that you will never find out if this happens. Everything will work just fine and the user will think that he selected the node he wanted to. This might be the case, but he might as well have chosen the wrong node.
So in short, you can get rid of the nid in the autocomplete callback, but it has 2 drawbacks:
performance (little)
uncertainty in selecting the correct node.
So you have to think about it, before going this route. Especially, since you most likely wont be able to find the problem of the selection of the wrong nodes, should it happen. Another thing to be aware of, is that the nid showing up, also brings some valuable info to the users, a quick way to lookup the node, should they be in doubt if it is the one they want, if several nodes have similar titles.
I got Grayside's answer to work, but I had to use MENU alter, instead of the FORM alter he posted. No biggy!
function custommodule_menu_alter(&$items) {
$items['nodereference/autocomplete']['page callback'] = 'fp_tweaks_nodereference_autocomplete';
}
I've found an alternative solution is to change your widget type to select list and then use the chosen module to convert your list to an autocomplete field.
This handles nodes with the same title, and actually I think the UI is better than the one provided by the autocomplete widget.
To anyone coming across this (rather old) topic by way of a google search - for Drupal 7 please consider using entityreference module and "Entity Reference" field type if possible.
You can acheive a lot more in configuration with an "Entity Reference" field. It doesn't have this problem with the nid in square brackets.
Here is the full Drupal 7 version (References 7.x-2.1) of Grayside's answer. This goes in your custom module:
/**
* Implementation of hook_menu_alter().
*/
function custom_menu_alter(&$items) {
$items['node_reference/autocomplete/%/%/%']['page callback'] = 'custom_new_node_reference_autocomplete';
}
/**
* Implementation of Menu callback for the autocomplete results.
*/
function custom_new_node_reference_autocomplete($entity_type, $bundle, $field_name, $string = '') {
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle);
$options = array(
'string' => $string,
'match' => $instance['widget']['settings']['autocomplete_match'],
'limit' => 10,
);
$references = node_reference_potential_references($field, $options);
$matches = array();
foreach ($references as $id => $row) {
// Markup is fine in autocompletion results (might happen when rendered
// through Views) but we want to remove hyperlinks.
$suggestion = preg_replace('/<a href="([^<]*)">([^<]*)<\/a>/', '$2', $row['rendered']);
// Add a class wrapper for a few required CSS overrides.
$matches[$row['title']] = '<div class="reference-autocomplete">' . $suggestion . '</div>'; // this is the line that was modified to remove the "[nid:XX]" disambiguator
}
drupal_json_output($matches);
}

Resources