Reusing Views to display predefined results

As a Web developer, I spend most of my time rendering lists of objects to HTML, formatted in every imaginable way. Fortunately for us Drupal heads, the Views module exists just to make this task easier - this is in fact my standard introduction to my "Views for hackers" talk.

One common scenario that I've encountered, and about which I was asked several times, is how to reuse Views to render a pre-defined list of objects - i.e., a list that already exists without the need for an SQL query. Why would anyone want to use Views in that case? Well, Views is not only a query generator but also a list renderer, and that's what interests us here. Furthermore, although we have a predefined list of object IDs (e.g., nids, uids), the actual rendering will surely need other fields and Views can give us those additional fields with very little effort on our part. Sounds good!

In my scenario, I had a content type that refers to users through various multi-valued CCK user reference fields. I needed to render all these users in a block on the node page, as a grid of user pictures that are preprocessed by ImageCache. The thought of hand-coding this theme (and later fixing it every time the client would change his mind about a detail) would have been enough to send me down an existential rabbit-hole, so I reacted quickly by reaching for Views and thinking hard about how it could be used. Fortunately, the answer was simple: default arguments to the rescue!

The main idea is to create a User view with an argument of type User: Uid to which we provide a default value based on PHP code (Provide default argument > PHP Code). The PHP code looks something like this:

= node_load(arg(1));
$uids = array();
// Replace this with your actual fields.
foreach ($node->field_users as $item) {
  if (!empty(
$item['uid'])) $uids[] = $item['uid'];
implode('+', $uids);

Also make sure to check the "Allow multiple terms per argument" option for this argument. The argument we created supplies to Views a list of uids against which to filter, so the resulting SQL statement would look like:

FROM {users}
WHERE uid IN (<the $uids list above>)

You can then configure the rest of your view exactly as you would otherwise. Now I can spend more time minding my precious media collection!


Thanks for the write up, my

Thanks for the write up, my approach is that if it's a list, then it should almost always be a view, there are just so many advantages to using views.

We tend to keep the logic of collecting the values for the argument out of the view itself, and wrap it in a custom block or something, that passes the correct string of arguments in, but your approach is equally valid.

I assume that this line:

= node_load(arg(1));

was a typo, and you actually meant:

= menu_get_object();

This would be a hard typo to

This would be a hard typo to make :-) I guess I just tend to forget the handy menu functions!

So you can do this with no

So you can do this with no custom PHP code at all.

Make a 'node' view. Add a 'Node: nid' argument and set the default to 'Node ID from URL'. Then, assuming the user reference field supplies the proper relationship (and I'd be shocked if it does not) add a relationship on that field. Now add whatever user fields you want, setting them all to use the relationship. You should get one record for every field. You can even sort them on the field delta, I believe, ensuring they come in the order that the field has them in.

that is brilliant merlin,

that is brilliant merlin, thanks for the insight.

Thanks for

Thanks for participating!

Your solution would work in the case of one user reference field. In my real-life case, I had 3 different user reference fields, so the relationship approach would not have worked - in fact, I started thinking along this line but abandoned it. In addition, the approach I describe here works for user lists (or any other entity list) obtained via arbitrary means, not only stored in CCK fields.

And yes, I still haven't

And yes, I still haven't figured out how to keep the view results sorted with the same order as the input!

You'd have to manually sort

You'd have to manually sort them via hook_views_pre_render() and change the actual order of $view->result

...and the solution would be

...and the solution would be to add a sort handler that uses the MySQL FIND_IN_SET function.

Using the newly-released

Using the newly-released Views PHP, the Global: PHP sort handler allows to write PHP code that generates an ORDER BY clause. I used this to return a clause like

FIND_IN_SET(nid, '{$this->view->args[0]}')

which orders the results according to the arguments that were passed to the view.