While learning to build Drupal (vers 6) modules and learning to use Views (vers 2) I have created a node_example.views.inc file which gives views access to the node_example table.
node_example.module is one of the main demonstration programs used within Drupal api documentation for teaching people to create modules. node_example.module demonstrates how to create a module that provides a new node type. It adds a new table to the database. The new table contains a color, a quantity, nid and vid.
Having created this views.inc file, I expect it to be useful to others learning to create modules or learning to create views for their modules. Before I set it up as as an optional extra on the node_example documentation, I would like to get it reviewed here.
Please tell me if you think it a reasonable implementation/documentation to add as a demonstration.
I also have one question relating to it. Having implemented it and spent some time playing with it within views there is one thing I haven't figured out how to do. That is, how to create a view with information from the node_example table such as color or quantity that only shows the latest versions of each node. That is restrict the view to have only distinct nid when there are multiple versions of some nodes. I presume that this is similar to the set up a view with one (latest) blog entry per user problem, which it appears that views can't handle. Could someone please confirm that it can't be done or tell how to do it.
Expected node_example documentation as follows:
For the new table defined by the node_example module to be understood by the views module you need to create a node_example.views.inc file that describes the table and its relationships to the rest of the database. In order for views to know that this file is to be loaded you need to implement the hook_views_api by adding the following function into you node_example.module file
<?php
function node_example_views_api() {
return array('api' => 2.0);
}
?>The node_example.views.inc file contains the following code:
<?php
// $Id:
/*
* This file is used to tell the views module about the new node_example table.
*
* Database definition:
* @code
* CREATE TABLE node_example (
* vid int(10) unsigned NOT NULL default '0',
* nid int(10) unsigned NOT NULL default '0',
* color varchar(255) NOT NULL default '',
* quantity int(10) unsigned NOT NULL default '0',
* PRIMARY KEY (vid, nid),
* KEY <code>node_example_nid</code> (nid)
* )
* @endcode
*/
function node_example_views_data() {
// Basic table information.
// ----------------------------------------------------------------
// node_example table
// New group within Views called 'Example'
$data = array();
$data['node_example']['table']['group'] = t('Example');
// New base table called 'Example Node'
// This allows it to be selected as the 'view type'
// when you initially add a new view.
$data['node_example']['table']['base'] = array(
'field' => 'vid',
'title' => t('Example Node'),
'help' => t("Example Node type with color and quantity information."),
'weight' => -9,
);
$data['node_example']['table']['join'] = array(
'node_revisions' => array(
'left_field' => 'vid',
'field' => 'vid',
),
'node' => array(
'left_field' => 'nid',
'field' => 'nid',
),
);
$data['node_example']['vid'] = array(
'title' => t('Node revision'),
'help' => t('The particular node revision the order color and quantity is attached to'),
'relationship' => array(
'label' => t('Node revision'),
'base' => 'node_revisions',
'base field' => 'vid',
// This allows us to not show this relationship if the base is already
// node_revisions so users won't create circular relationships.
'skip base' => array('node', 'node_revisions'),
),
);
$data['node_example']['nid'] = array(
'title' => t('Node'),
'help' => t('The particular node the order color and quantity is attached to'),
'relationship' => array(
'label' => t('Node'),
'base' => 'node',
'base field' => 'nid',
// This allows us to not show this relationship if the base is already
// node so users won't create circular relationships.
'skip base' => array('node', 'node_revisions'),
),
);
// quantity field
$data['node_example']['quantity'] = array(
'title' => t('Quantity'),
'help' => t('Order Quantity.'),
'field' => array(
'handler' => 'views_handler_field_numeric',
'click sortable' => TRUE,
),
'filter' => array(
'handler' => 'views_handler_filter_numeric',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
);
// Color field
$data['node_example']['color'] = array(
'title' => t('Color'),
'help' => t('Color of item ordered.'),
'field' => array(
'handler' => 'views_handler_field',
'click sortable' => TRUE,
),
'filter' => array(
'handler' => 'views_handler_filter_string',
),
'argument' => array(
'handler' => 'views_handler_argument_string',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
);
return $data;
}
?>Some notes on usage:
Within the Vers 2 Views, click on the Add tab. You have a number of type options here. If you only want nodes of the new example_node type within your view then select the new Example node type. This makes example_node your primary table. If you want all nodes including the example nodes in your view then select Node as your type.
If you selected Example node as your view type, when you initially go into the edit window of views you will find that the only fields available for field type are the color and quantity fields. To get fields from other tables you need to add a relationship. Relationships is at the top in the same column as the fields.
Comments
Great start!
Ok, first, I have to say, I love this, I'm very very excited to see it and I will more than happily integrate it into the advanced help so that we have a great tutorial on this.
However (always a hitch) in a quick review, your relationship examples are unfortunately confusing. The problem is, they are providing relationships that are useless, so that while they provide a technical example, someone trying to understand what the relationship is actually doing will completely fail. In order to have those relationships be meaningful, you would need node_example to have a link to some other table so that we can explain how things work when that other table is the base table. A uid would be a fantastic example, since we could then explain how you can go from a base user with that uid to the node(s) and vice versa.
Reply to merlinofchaos
First, I should state that this project for me is just a views add on to the current Drupal API Documentation for the node_example.module demonstration. In this demonstration, I don't want to change the current node_example.module by adding any extra fields to their database, regardless of whether it would improve the views demonstration.
I'm very flattered that you are even thinking of using it for the advanced help. If this required adding in a new field to the data base though, then I expect that it should be an extension or extra project.
I'm surprised at your comment on the 'relationships' being useless. If the user picks 'Example node' type (that is sets node_example as the base table) and there wasn't any relationships, the user would then only be able to select the fields of quantity and color. The user would have no access to any of the data in any of the tables. It would make the 'Example node' type useless.
There is an argument that there is no need at all for the 'Example node' type (and consequently for the relationships) as you can generally produce the same views starting by selecting the 'Node' type or 'Node Revision' type and then filter back to just those nodes that have data in the node_example table. The problem with this is that it appears to me to be very inefficient. In this case we are pulling out of the database data relating to every single node and then filtering all except a few out. It's a lot more efficient to only pull out of the database data for those that are in the node_example table. For this we need the node_example table to be the primary table which then requires the extra base and the relationships.
One thing I could do is split this into two demonstrations. A minimum one and an expanded more efficient one.
I have also found a few mistakes in the above documentation such as reversing 'node' and 'example' etc.
I have to admit that as I was doing it I was very confused with the fact that this table has two indexes. i.e. both vid and nid. I would expect this vid/nid double index to be common for modules but I searched all the .views.inc files within the views package itself and the few I had in other modules and couldn't find any others like it. I was confused with the join data. For example in the Node table there is both vid and nid fields. Should I join both. In the end I reduced it to just a single join to each of the Node and Node revision tables. It works like that so I am not sure what advantage extra joins would have yet I see them occurring in other .views.inc files.
I'll update the documentation and see if you prefer it.
Regards
Ken
www.ausvalue.com
The thing is, the
The thing is, the node_example is basically a direct relationship to the 'node' table. There's no point (other than as an example) to even having it as a base table.
Base tables are reserved primarily for two things: First class data and the occasional glue tables; the glue tables aren't actually visible as base tables, either, instead they're used as points where relationships can be added. (This is a difficult concept, but it's something nodequeue uses to good effect).
node_example table is not really first class data; it's dependent upon the node. Having it as a base table is actually actively bad. So unfortunately, that makes node_example a poor example if that is the kind of thing that you want to do.
Your example about inefficiency isn't very compelling, either; there is already a good index on node.type and it costs very little to filter by type. Likewise, do other node types have their own base tables? Poll has its own data, yet has no 'poll node' as a base table. Nor any CCK node derivative. In fact, if we felt that each node type that has unique data should have its own base table, the list of base tables would get very cluttered in a hurry. This would be inefficient in a completely different direction.
The reason it has vid and nid is that it's an example of retaining revision data. In fact, the join to node should be on 'vid' so that you only get the current revision; if you join on 'nid' then you will get all revisions. If you look you will note that taxonomy
s term_node table, for example, joins to the node on vid, rather than nid, so that you only get the current taxonomy terms.
Updated version
Expected node_example documentation as follows:
For the new table defined by the node_example module to be understood by the views module you need to create a node_example.views.inc file that describes the table and its relationships to the rest of the database. In order for views to know that this file is to be loaded you need to implement the hook_views_api by adding the following function into your node_example.module file
<?phpfunction node_example_views_api() {
return array('api' => 2.0);
}
?>
Contents of a simple node_example.views.inc file that will allow you to create Vers 2 views that include the new color and quantity information is as follows
<?php
// $Id:
/*
* This file is used to tell the views module about the new node_example table.
*
* Database definition:
* @code
* CREATE TABLE node_example (
* vid int(10) unsigned NOT NULL default '0',
* nid int(10) unsigned NOT NULL default '0',
* color varchar(255) NOT NULL default '',
* quantity int(10) unsigned NOT NULL default '0',
* PRIMARY KEY (vid, nid),
* KEY <code>node_example_nid</code> (nid)
* )
* @endcode
*/
function node_example_views_data() {
// Basic table information.
// ----------------------------------------------------------------
// node_example table
// New group within Views called 'Example'
$data = array();
$data['node_example']['table']['group'] = t('Example');
// tables + fields that can be used for SQL Joins
$data['node_example']['table']['join'] = array(
'node_revisions' => array(
'left_field' => 'vid',
'field' => 'vid',
),
'node' => array(
'left_field' => 'nid',
'field' => 'nid',
),
);
// quantity
$data['node_example']['quantity'] = array(
'title' => t('Quantity'),
'help' => t('Quantity of items.'),
'field' => array(
'handler' => 'views_handler_field_numeric',
'click sortable' => TRUE,
),
'filter' => array(
'handler' => 'views_handler_filter_numeric',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
);
// Color
$data['node_example']['color'] = array(
'title' => t('Color'),
'help' => t('Color of item.'),
'field' => array(
'handler' => 'views_handler_field',
'click sortable' => TRUE,
),
'filter' => array(
'handler' => 'views_handler_filter_string',
),
'argument' => array(
'handler' => 'views_handler_argument_string',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
);
return $data;
}
?>
Some notes on usage:
Within the Vers. 2 Views, click on the Add tab. You have a number of type options here. Normally you would select either 'Node' or 'Node revision'. If one of the fields you want to display is vid then I suggest that you select 'Node revision'. If you select 'Node' as the type (making the node table the base table) then you are likely to find an incorrect vid when displaying any older node that has been revised.
Regardless of whether the type you select is 'Node' or 'Node revision', with this configuration you will always pull out of the database, data for every single node, whether or not it has color and quantity information. To display information on just those nodes that have color and quantity information you can use a filter so that only nodes which don't have a NULL color or a NULL quantity are displayed.
Pulling data out of the database for every node when you only want data for the new Example node type is inefficient. To reduce the initial data extraction to just that relating to the new Example nodes requires that you make the node_example table the base table. This can be done by adding the following code into the node_example.views.inc file just before the 'return $data;'
<?php
// **** Begin optional extra for type and relationships
// Use node_example as a new base table
// by creating a new views type called 'Node example'
// This allows it to be selected as the 'view type'
// when you initially add a new view.
$data['node_example']['table']['base'] = array(
'field' => 'vid',
'title' => t('Node example'),
'help' => t("Node example type with color and quantity information."),
'weight' => -9,
);
// When using the new 'Node example' type you need to use relationships
// to access fields in other tables.
// Relationship to the 'Node revision' table
$data['node_example']['vid'] = array(
'title' => t('Node revision'),
'help' => t('The particular node revision the color and quantity is attached to'),
'relationship' => array(
'label' => t('Node revision'),
'base' => 'node_revisions',
'base field' => 'vid',
// This allows us to not show this relationship if the base is already
// node_revisions so users won't create circular relationships.
'skip base' => array('node', 'node_revisions'),
),
);
// Relationship to the 'Node' table
$data['node_example']['nid'] = array(
'title' => t('Node'),
'help' => t('The particular node the color and quantity is attached to'),
'relationship' => array(
'label' => t('Node'),
'base' => 'node',
'base field' => 'nid',
// This allows us to not show this relationship if the base is already
// node so users won't create circular relationships.
'skip base' => array('node', 'node_revisions'),
),
);
// **** End optional extra for type and relationships
?>
The above code adds a new 'Node example' to the view types that can be selected within the Add tab window of views. Selecting this sets the node_example table to be the base table.
If you select 'Node example' as view type, when you initially go into the edit window of views you will find that the only fields available are the color and quantity fields. To get fields from other tables you need to add a relationship. Relationships is at the top in the same column as the fields.
Hope this helps
Ken
www.ausvalue.com
Hopefully final version.
As you can probably recognize, I'm not a database expert. I'm using this exercise as a learning tool. Consequently, I'm not the right person to extend it.
Thank you for the help. Changing the nid to vid in the join improves it drastically. I have revised it again and I believe I have incorporated the information you have given me. Hopefully this will be helpful to some, even if it doesn't give the full example that you want.
Below is what I hope to make the views documentation for the current node_example.module. I will leave it here for a few days and if no one comes up with any more mistakes I will place into the api.drupal.org documentation.
For the new table defined by the node_example module to be understood by the views module you need to create a node_example.views.inc file that describes the table and its relationships to the rest of the database. In order for views to know that this file is to be loaded you need to implement the hook_views_api by adding the following function into your node_example.module file
<?phpfunction node_example_views_api() {
return array('api' => 2.0);
}
?>
Contents of a simple node_example.views.inc file that will allow you to create Vers 2 views that include the new color and quantity information is as follows
<?php
// $Id:
/*
* This file is used to tell the views module about the new node_example table.
*
* Database definition:
* @code
* CREATE TABLE node_example (
* vid int(10) unsigned NOT NULL default '0',
* nid int(10) unsigned NOT NULL default '0',
* color varchar(255) NOT NULL default '',
* quantity int(10) unsigned NOT NULL default '0',
* PRIMARY KEY (vid, nid),
* KEY <code>node_example_nid</code> (nid)
* )
* @endcode
*/
function node_example_views_data() {
// Basic table information.
// ----------------------------------------------------------------
// node_example table
// New group within Views called 'Example'
$data = array();
$data['node_example']['table']['group'] = t('Example');
// tables + fields that can be used for SQL Joins
$data['node_example']['table']['join'] = array(
'node_revisions' => array(
'left_field' => 'vid',
'field' => 'vid',
),
'node' => array(
'left_field' => 'vid',
'field' => 'vid',
),
);
// quantity
$data['node_example']['quantity'] = array(
'title' => t('Quantity'),
'help' => t('Quantity of items.'),
'field' => array(
'handler' => 'views_handler_field_numeric',
'click sortable' => TRUE,
),
'filter' => array(
'handler' => 'views_handler_filter_numeric',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
);
// Color
$data['node_example']['color'] = array(
'title' => t('Color'),
'help' => t('Color of item.'),
'field' => array(
'handler' => 'views_handler_field',
'click sortable' => TRUE,
),
'filter' => array(
'handler' => 'views_handler_filter_string',
),
'argument' => array(
'handler' => 'views_handler_argument_string',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
);
return $data;
}
?>
Some notes on usage:
Within the Vers 2 Views, click on the Add tab. You have a number of type options here. Normally you would select either 'Node' (if you only want to display information on current nodes) or 'Node revision' (if you want to display information on all revisions of the nodes)
With this configuration you always pull out of the database, data for every single node, whether or not it has color and quantity information. To display information on just those nodes that have color and quantity information you can use a filter so that only nodes which don't have a NULL color or a NULL quantity are displayed.
TYPE/RELATIONSHIP EXTENSION.
When your tables have first class data, you will often want to have own View types and View relationships defined. With the current node_example table this isn't required although I try to justify it below on an efficiency basis. See this discussion as to why it isn't justified.
Pulling data out of the database for every node when you only want data for the new Example node type is inefficient. To reduce the initial data extraction to just that relating to the new Example nodes requires that you make the node_example table the base table. This can be done by adding the following code into the node_example.views.inc file just before the 'return $data;'
<?php
// **** Begin optional extra for type and relationships
// Use node_example as a new base table
// by creating a new views type called 'Node example'
// This allows it to be selected as the 'view type'
// when you initially add a new view.
$data['node_example']['table']['base'] = array(
'field' => 'vid',
'title' => t('Node example'),
'help' => t("Node example type with color and quantity information."),
'weight' => -9,
);
// When using the new 'Node example' type you need to use relationships
// to access fields in other tables.
// Relationship to the 'Node revision' table
$data['node_example']['vid'] = array(
'title' => t('Node revision'),
'help' => t('The particular node revision the color and quantity is attached to'),
'relationship' => array(
'label' => t('Node revision'),
'base' => 'node_revisions',
'base field' => 'vid',
// This allows us to not show this relationship if the base is already
// node_revisions so users won't create circular relationships.
'skip base' => array('node', 'node_revisions'),
),
);
// Relationship to the 'Node' table
$data['node_example']['nid'] = array(
'title' => t('Node'),
'help' => t('The particular node the color and quantity is attached to'),
'relationship' => array(
'label' => t('Node'),
'base' => 'node',
'base field' => 'nid',
// This allows us to not show this relationship if the base is already
// node so users won't create circular relationships.
'skip base' => array('node', 'node_revisions'),
),
);
// **** End optional extra for type and relationships
?>
The above code adds a new 'Node example' to the view types that can be selected within the Add tab window of views. Selecting this sets the node_example table to be the base table.
If you select 'Node example' as view type, when you initially go into the edit window of views you will find that the only fields available are the color and quantity fields. To get fields from other tables you need to add a relationship. Relationships is at the top in the same column as the fields.
You will find a lot of information on creating views.inc files within the Views Advanced help when you install both the Views module and the Advanced help module on your drupal site. You will also find a number of views.inc files within the views module (in views/modules directory) that can be used as a guide.
Hope this helps
Ken
www.ausvalue.com
thank you
Ken et al. your comments were very helpful to me. I really made progress with my first Drupal views project.
I included a link on the page http://drupal.org/node/109604.
Node type
Just for clarification:
You can also set up views to filter out any node type that does not match "node_example."
Is this any less efficient than making "node_table" a base table? It seems like the filtering happens on the SQL level, so other node types wouldn't be returned anyway.
Congrats :)
This is exactly what i was looking for since months and i could not find this jewel
(i maybe had to read the views advanced help earlier :) )
thank you
Cache needs to be cleared
After creating info, module, and views.inc files, and enabled the module, went on to the Views page and created a view, but no new view type showed up. Spent a few days to look for other tutorials/manuals, but no avail.
Then, I've found that I missed the following statement in Views 2 advanced help:
"Then visit the Views tools page and clear the Views cache."
I cleared the cache (Admin->Site config->Performance and "Clear cached data"), and then the new view type appeared!
Thanks.