Hi! I have a module I've developed that keeps lists of users subscribed to a mailing list. This is all done without referencing the node table. I would like to be able to use the insert_view module to allow authors to do something like [view:mailing_list_users==my_mailing_list], and have it be replaced with a nicely formatted list of users subscribed to the list. This would be easy to do if I could have the base table be a table other than 'node'. It looks from views_query.inc that this would be possible. While making the UI be able to work with this would be a pain, for modules that are defining their own view it would seem to be something that wouldn't be too hard. Primarily it would seem to be adding two variables to the $view object passed to _views_build_query, and if they're defined, passing those to the call to "new _views_query()". Am I way off? Would this actually be hard?

Comments

yched’s picture

Status: Active » Closed (duplicate)
pukku’s picture

Hm. But, I'm not looking for _arbitrary_ views to be able to do this. I'm looking for views provided by modules, which don't need a UI to set up, to be able to do this. And I know that the _views_query class can build the queries, because I've played around (using the devel module) to make it generate some arbitrary queries not based off of node.

Tomorrow, when I'm back in a place where I can actually do development (my dev server is locked away behind a firewall from home), I'll see if I can put together an example.

(it would be nice if I could reset the status to 'submitter is working on a response', rather than leaving it as duplicate, or setting it to active, but such is life)

merlinofchaos’s picture

Default views are just views data that's loaded from code, not the DB; how they are built is exactly the same. They cannot have abilities that regular views cannot.

pukku’s picture

Status: Closed (duplicate) » Active

OK, here's an example of what I'm looking at.

Suppose we have a module called 'lists'. It has a table:

CREATE TABLE lists (
    lid integer,
    title varchar(50)
);
INSERT INTO lists (lid, title) VALUES (1, 'test 1');
...

In real life, this table has a bunch of other stuff in it, but we don't need to worry about that right now.

Also, let's apply the following diff to views_query.inc:

--- views_query.inc.orig	2006-09-29 12:09:20.000000000 -0400
+++ views_query.inc	2006-09-29 11:59:44.000000000 -0400
@@ -5,7 +5,12 @@
  * This builds a query for the view.
  */
 function _views_build_query(&$view, $args = array()) {
+  if (isset($view->title_table)) {
+    $query = new _views_query($view->title_table, $view->title_field);
+  }
+  else {
     $query = new _views_query();
+  }
 
   // Process static filters
   _views_view_build_filters($query, $view);

There could be better ways of doing this, but I'm just trying for a demonstration right now, so this is sufficient.

Next, we're going to define the following functions:

function lists_views_tables() {
}

function lists_views_arguments() {
}

function lists_views_default_views() {
    $view = new stdClass();
    $view->name = 'lists_test';
    $view->description = 'Show the lists';
    $view->page = true;
    $view->page_title = 'Lists';
    $view->page_header = '';
    $view->page_header_format = '1';
    $view->page_footer = '';
    $view->page_footer_format = '1';
    $view->page_empty = 'No lists available';
    $view->page_empty_format = '1';
    $view->page_type = 'list';
    $view->url = 'lists_test';
    $view->use_pager = false;
    $view->nodes_per_page = '100';
    
    $view->field = array(
        array(
            'tablename' => 'lists',
            'field' => 'title',
            'label' => '',
        ),
    );
    
    $view->title_table = 'lists';
    $view->title_field = 'lid';
    
    return array($view->name => $view);
}

If you then clear your cache, and go to admin/views, you'll see the new view there. You can go to the URL it exposes, and you'll get a list of all the lists available.

We can improve on this. Suppose I have a table:

CREATE TABLE lists_membership (
    lid integer,
    uid integer
);

This table would be populated with the uids of users who are on this mailing list.

Now, let's redefine hook_views_tables():


function lists_views_tables() {
    $tables = array();
    $tables['lists'] = array(
        'name' => 'lists',
        'fields' => array(
            'lid' => array(
                'name' => 'Lists: List ID',
            ),
            'title' => array(
                'name' => 'Lists: List Title',
            ),
            'members' => array(
                'name' => 'Lists: Members',
                'notafield' => true,
                'handler' => 'lists_handler_field_members',
            ),
        ),
    );
    
    return $tables;
}

function lists_handler_field_members($fieldinfo, $fielddata, $value, $data) {
    $output = '<ul>';
    $mem_query = db_query("
            SELECT u.name 
            FROM {lists_membership} as m
                INNER JOIN {users} as u ON m.uid = u.uid
            WHERE m.lid = %d", $data->lid);
    while($mem_res = db_fetch_array($mem_query)) {
        $output .= '<li>' . $mem_res['name'] . '</li>';
    }
    $output .= '</ul>';
    
    return $output;
}

This exposes an extra, 'notafield' field, which uses a handler to get a list of everyone subscribed to the list, and return a unordered list of their user names.

Let's also redefine our default view:

function lists_views_default_views() {
    $view = new stdClass();
    $view->name = 'lists';
    $view->description = 'Show the lists';
    $view->page = true;
    $view->page_title = 'Lists';
    $view->page_header = '';
    $view->page_header_format = '1';
    $view->page_footer = '';
    $view->page_footer_format = '1';
    $view->page_empty = 'No lists available';
    $view->page_empty_format = '1';
    $view->page_type = 'list';
    $view->url = 'lists';
    $view->use_pager = false;
    $view->nodes_per_page = '100';
    
    $view->field = array(
        array(
            'tablename' => 'lists',
            'field' => 'title',
            'label' => '',
        ),
        array(
            'tablename' => 'lists',
            'field' => 'members',
            'label' => '',
        ),
    );
    
    $view->title_table = 'lists';
    $view->title_field = 'lid';
    
    return array($view->name => $view);
}

This adds the 'members' field to the view.

If you now clear your cache, and look at the view, you'll see a list of the lists, and for each list it will also have a list of the users subscribed to the list.

This is what I was trying to get at, and I think I've shown that it's possible.

So why do I want this?

I want to be able to have a page for each list, which, among other things, lists the users subscribed to each list. Further, I want other people, people who don't speak HTML or PHP, be able to edit this page. The insert_view module has pretty much the exact interface I would like — and my users who would be editing this page can understand why this thing is there. Right now, my solution involves letting them enter their own PHP code, which I give them as a "magic formula", which calls a function to generate this list. This is a bit dangerous, and I'd rather not allow them to insert PHP. So if I can provide a view which they could use to embed the data, I would be much happier.

I realize that there is no easy way to make a user interface which would allow people to build their own views in this way, but in cases where the module is providing the view as a default view, the module developer can understand what's going on, and make sure it works before they send it out. This is why my suggestion is to make this possible for default views, but not for user developed views.

Is this a better explanation? Does this make sense?

Thanks,
Ricky

merlinofchaos’s picture

Status: Active » Closed (works as designed)

No. I'm not going to put in exceptions for default views to act differently from database views. That is a key component of the design.

pukku’s picture

Fair enough; it's your project...

Thanks,
Ricky