Dynamic customization of views, but also need support for views ajax

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
markus_petrux's picture

Hi,

This is about a problem I have found when trying to write a feature for a module that needs to provide back reference views to nodes based on CCK node reference fields.

What I would like this to work is that the module provides a base default view that is used to provide all back refence views. This view can be modified to add more fields, change the style, add and/or expose filters, and so on. The user can also clone this view and assign the customized clone to any particular back reference relation.

The module gets the view assigned to each back reference and dynamically adds the views relationship with the nodereference field and 2 arguments. One is used to filter by node type, and the other is used to filter by nid of the node where this view is going to be rendered.

It works just fine, but I have a problem when AJAX support is enabled to the view. The ajax views code provides its own menu callback to resolve the ajax requests, and there I don't have a chance to re-apply the dynamic modification the view needs in order to add the relationship and arguments that are needed to provide the desired results.

This is how I build the view:

<?php
 
if (($view = views_get_view($view_name)) && $view->access($display_id)) {
   
$view->set_display($display_id);
   
$handler = &$view->display_handler;
   
// Here's where I add the relationship and 2 arguments to the default view.
   
foreach (noderelationships_get_backref_view_overrides($referrer_field) as $option => $definition) {
     
$handler->override_option($option, $definition);
    }
   
// Use a different pager element per view in a given page request.
   
$handler->override_option('pager_element', $pager_element);

    return
$view->preview($display_id, array($referred_node->nid, $referrer_type->type));
  }
?>

But this code is not executed when the ajax view request is processed. Instead, the view is rendered as it is on the database, without the customized relationship and 2 arguments.

I have tried using hook_views_pre_view() to alter the view, but it doesn't seem to work. :(

So what else can I try? I guess this kind of views management can open a lot of possibilities as you can have views like templates that are customized in realtime depending on context. Otherwise, the views repository would end up too big.

Maybe one possible way to address this would be if the function views_ajax() could invoke something like drupal_alter('views_ajax', $view, $args) after $view-access() or before $view->validate() ???

Anyone tried something similar?

Comments

I got it, but it looks hack-ish, I think...

markus_petrux's picture

Ok, I got it working using hook_views_pre_view(). Here I can modify the view to add the required relationships and arguments, but first I need to unset($view->current_display);

Here's what I do now:

<?php
function noderelationships_views_pre_view(&$view, &$display_id, &$args) {
 
// First, check if this is a view we need to alter.
 
...
  ...

 
// This is necessary so that views can take the changes we are introducing here.
 
unset($view->current_display);
 
$view->init_display();

 
// Load the related field.
 
$referrer_field = content_fields($field_name, $type_name);

 
// Alter the view to add custom relationship and arguments.
 
$handler = &$view->display_handler;
  foreach (
noderelationships_get_backref_view_overrides($referrer_field) as $option => $definition) {
   
$handler->override_option($option, $definition);
  }
}
?>

How does it look? Is this unset($view->current_display); a hack? Any better way to do it?

On the other hand, I also tried adding a drupal_alter to views_ajax(). Here's the mini-patch to views/includes/ajax.inc:

<?php
        
// Fix 'q' for paging.
        
if (!empty($path)) {
          
$_GET['q'] = $path;
         }
+
+       
// Allow other modules alter the view before it is executed.
+        drupal_alter('ajax_view', $view, $args);

        
// Override the display's pager_element with the one actually used.
        
if (isset($pager_element)) {
          
$view->display[$display_id]->handler->set_option('pager_element', $pager_element);
         }
?>

And here's my hook_ajax_view_alter() implementation:

<?php
function noderelationships_ajax_view_alter(&$view, &$args) {
 
// First, check if this is a view we need to alter.
 
...
  ...

 
// Load the related field.
 
$referrer_field = content_fields($field_name, $type_name);

 
// Alter the view to add custom relationship and arguments.
 
$handler = &$view->display_handler;
  foreach (
noderelationships_get_backref_view_overrides($referrer_field) as $option => $definition) {
   
$handler->override_option($option, $definition);
  }
}
?>

I think I like this secons method most than the previous. Here, I do not need to unset($view->current_display);, but it needs a patch to views.

Which method looks better and less prone to conflicts with views and other extensions?

Patch for review

markus_petrux's picture

I have posted a patch to the views queue that adds a drupal_alter() call to views_ajax() as described in the second method above.

http://drupal.org/node/274792

I really think it makes things easier to extend the possibilities of views.

markus_petrux's picture

Anyone interested on an example of dynamically customized views with default and page displays and ajax enabled, check out the stuff I've been writing for node back reference views in this module:

http://drupal.org/project/noderelationships

Tricky, but it works nicely. :)

Cheers

Interesting

awm's picture

This is interesting. I stumbled upon this thread as I have been searching a way to actually alter a view dynamically. For example, say you have a view that displays all the nodes where a certain cck field have the value '1' by default. Now say you need to dynamically alter this criteria based on other conditions i.e.

if(year == 2010) {// display all the nodes   where a certain cck field have the value '1'}
else {// display all the nodes   where a certain cck field have the value '2'}

I feel like your example above could be helpful but cannot see how yet and especially that I am working with D7. It would really be great if you can point me in the right direction..

Cheers

Views Developers

Group organizers

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds:

Hot content this week