I am developping a module for attaching notes to nodes. The notes are saved per user. Now I would like to create a table-view of all nodes (from a particular type) with only the notes of the currently logged in user shown.
In my module I've implemented hook_views_api() and hook_views_data() so I could make a relation with my note-table. I want to view all nodes, also the ones with no note attached.
The problem I have is that currently only the nodes with notes are shown, probably because of the filter 'Currently logged in user'.
If I remove that filter, all nodes are shown, but also notes of other users are visible in this case.
The table with the notes (simplified):
CREATE TABLE IF NOT EXISTS `note_node` (
`nid` int(11) NOT NULL,
`uid` int(11) NOT NULL,
`notes` text NOT NULL,
PRIMARY KEY (`nid`,`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Implementation of hook_views_data().
<?php
/**
* Implementation of hook_views_data().
* @return array
*/
function note_views_data()
{
$data = array();
// Define the name of the group where to the data belongs
$data['note_node']['table']['group'] = t('Notes');
// Create relation with node table
$data['note_node']['table']['join'] = array(
'node' => array(
'left_field' => 'nid',
'field' => 'nid',
'type' => 'LEFT',
),
);
// User ID field
$data['note_node']['uid'] = array(
'title' => t('Note user id'),
'help' => t('Author of the note'),
'field' => array(
'handler' => 'views_handler_field',
'click sortable' => TRUE,
),
'filter' => array(
'handler' => 'views_handler_filter_numeric',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
'relationship' => array(
'base' => 'users',
'field' => 'uid',
'handler' => 'views_handler_relationship',
'label' => t('Note user id'),
),
);
// Notes field
$data['note_node']['notes'] = array(
'title' => t('Notes'),
'help' => t('Just a plain text field.'),
'field' => array(
'handler' => 'views_handler_field',
'click sortable' => TRUE,
),
'sort' => array(
'handler' => 'views_handler_sort',
),
'filter' => array(
'handler' => 'views_handler_filter_string',
),
'argument' => array(
'handler' => 'views_handler_argument_string',
),
);
return $data;
}
?>
View settings:
Basis settings:
Name: Defaults
Title: None
Style: Table
Use AJAX: No
Use pager: No
Items to display: Unlimited
More link: No
Distinct: No
Access: Unrestricted
Caching: None
Exposed form in block: No
Header: None
Footer: None
Empty text: None
Theme: Information
Relationships:
Notes: Note user id
Filters:
Node: Type = Task
(Note user id) User: Current Yes
Fields:
Node: Title Title
Notes: Note user id Note user id
Notes: Notes Notes
Any thoughts on how I could easily get the right results?
The goal is to get a view with all nodes of nodetype 'Task' shown, together with the notes of the currenty logged in user. Notes of other users may not appear in the view.
| Comment | File | Size | Author |
|---|---|---|---|
| view-settings.png | 82.17 KB | megachriz |
Comments
Comment #1
dawehnerCan you also pastebin the query which views generates? Perhaps the query is already right :)
Comment #2
merlinofchaos commentedYes, what you're getting is precisely what you should expect.
You have a filter that is filtering to notes by the currently logged in user. If a node does not have a note, then that note's user is NULL. NULL is not the same as the currently logged in user. Therefore you will need a custom filter that can filter to the currently logged in user OR is NULL.
Comment #3
megachrizThanks for your replies. I took a look at the Views_Or module to get the 'OR is NULL' in the query, but that didn't work out.
It seems I need to have a lot more complicated query in order to get the desired results, a query with an UNION and a subselect.
I figured out that this query will result in the desired results:
In the querypart after the UNION I put in NULL for the columns 'note_node_uid' and 'note_node_notes' so both queryparts results in the same number of columns (or else MySQL will return an error).
And this was the query generated by Views:
Now I'm looking for a way to get the first query to be generated by Views, because for the website I'm developping I will need multiple simular views (for example a view of all nodes with the nodetype 'task' and the value of a CCK checkbox-field set to true).
Can this be done with Views?
Comment #4
megachrizComment #5
merlinofchaos commentedViews has no support for UNION, nor are there plans for it at this time.
The best you can do is use hook_views_pre_execute() to change the query (it's in $view->build_info) prior to execution. It's not deal but Views really isn't designed to do what you're trying to do.
Comment #6
megachrizThanks for your reply. I've took a look at the hook hook_views_pre_execute().
Because I need multiple views I need to find a good way to let Views generate the subqueries as well, because the number of fields is different per view.
I'm thinking of copying the view-object, manipulate the copy and let that copy generate a new query, which I will add to the main view-object.
I'm now trying to figure out how I can get the following query to be generated:
My attempts so far:
(I have prefixed the variable names so I know easily of which type the variable is)
The filter 'uid_current' was succesfully removed from the query, but I had no success yet to get 'NULL as note_node_notes' in the query.
What do I need to change on the view-object in order to get the query above? Do I need to implement other hooks as well?
Comment #7
megachrizI finally managed to let views generate the desired query. The UNION-part is pasted after the 'main' query views normally generate. To replace the field names with NULL in the UNION-part I now used str_ireplace(). The UNION-part now is generated in a function that implements hook_views_pre_build(). The output is saved in a Registry under the name of the view combined with the name of the current display. The output is retrieved in a function that implements hook_views_pre_execute() and then pasted in the query.
Now there is still one problem. When a user has restricted node access, the query gets altered after the generated query leaves my module. It adds the following directly after the WHERE statement:
The closing ')' is added at near the end of the query (before the ORDER BY) which results in a MySQL syntax error, because the closing ')' should be added before the UNION-statement.
I'm sure the query gets altered after it leaves my module, because the above SQL-code isn't yet present when the view-object enters my module function that implements hook_views_pre_execute().
I tried to change the weight of my module, but that didn't work out.
I took a look at the nodeaccess-module, which was enabled, but I didn't saw any view-hooks implemented there nor did I see implementations of hook_db_rewrite_sql(), which leaves me to think that the views module itself inserted it at some place.
The class 'views_handler_filter_node_access' I found doesn't seem to be used in this case, because I temporary added a 'die()' there and the page generation didn't get terminated.
So where and how does the above SQL-code gets inserted?
(Btw, I have upgraded to Views 6.x-2.10 now)
Comment #8
merlinofchaos commentedThat altering is the result of db_rewrite_sql() -- see node_db_rewrite_sql() for node.module's implementation. Good luck. (db_rewrite_sql() has been a pain in my ass for years).
Comment #9
megachrizThanks for your reply.
I had just figured this out myself. It is the db_rewrite_sql() function that breaks my query. I've tried to make a fix that works for now, but this will not work when there are other modules then the node-module implementing hook_db_rewrite_sql() that will alter my query.
This is my fix (comes from the class I had created to hold the Union-part of the query):
I used (!node_access_view_all_nodes() && _node_access_where_sql()) to check if the query will be altered by node_db_rewrite_sql().