I've made an additional field in the profile.module to enter and validate numbers. The profile.module stores its information using an EAV data model - therefore my numbers are being stored as text in the database. To make the numbers sort correctly they will need to be converted from text into numbers in the SQL statement...
I've discovered that MySQL 4.1 doesn't support CAST very well (there is no CAST AS DECIMAL), but you can trick MySQL into the conversion by adding 0 to the text value and MySQL will convert on the fly:
This statement will sort the numbers stored as text correctly:
SELECT value FROM {profile_values} WHERE fid = %d ORDER BY (value + 0) DESC
as opposed to:
SELECT value FROM {profile_values} WHERE fid = %d ORDER BY value DESC
So, I'm stuck with the specific question:
How do you write a views handler that changes 'value' to '(value +0)' ?
I've got this far:
function views_handler_filter_profile_number($op, $filter, $filterinfo, &$query) {
switch ($op) {
case 'handler':
$table = 'profile_value';
$column = 'value';
$query->ensure_table($table);
$field = "$table.$column";
$operator = $filter['operator'];
$where = "(%s + 0) $operator %s";
$query->add_where("$where", $field, $filter['value']);
$query->add_groupby('profile_value.value');
$filter->field = $field;
return $filter;
break;
}
}
But, for some reason the profile name is turning up in the SQL statement:
user warning: Unknown table 'profile_value' in where clause query: SELECT node.nid, profile_producer_total.value AS profile_producer_total_value, node.title AS node_title, node.changed AS node_changed, profile_producer_name.value AS profile_producer_name_value, profile_donated.value AS profile_donated_value FROM node node LEFT JOIN users users ON node.uid = users.uid LEFT JOIN profile_values profile_producer_total ON users.uid = profile_producer_total.uid AND profile_producer_total.fid = '4' LEFT JOIN profile_values profile_producer_name ON users.uid = profile_producer_name.uid AND profile_producer_name.fid = '2' LEFT JOIN profile_values profile_donated ON users.uid = profile_donated.uid AND profile_donated.fid = '3' WHERE (node.status = '1') AND (node.type IN ('usernode')) AND ((profile_value.value + 0) >= 10) AND ((profile_value.value + 0) < 100) GROUP BY profile_value.value, profile_value.value ORDER BY profile_producer_total_value DESC LIMIT 0, 50
I cannot figure out how to set the 2nd table to be the same as the first:
LEFT JOIN profile_values profile_values
My guess is that the routine that cycles through all the profile fields is getting messed up by my custom filter?
Please HELP! I've spent two days on this and I'm so close to adding a sweet feature to the profile.module.
Thanks so much for your help - I'm good for a beer (or two) for the solution.
Cheers, Jason
Comments
Comment #1
merlinofchaos commentedWell to start with, you've got the wrong kind of handler, which puts you in totally the wrong ballpark. You want a sort handler, not a filter handler.
First, I'm going to point you to the shiny new documentation (that I haven't made public but need to soon; was hoping more would get done on it but it's better unfinished and available than it is unfinished and unavailable) at http://drupal.org/handbook/modules/views/api
In the sorts section you'll find the info about the sort handler. DOn't worry about the $op, nothing really uses it.
Your handler should work very similar to how the random sort handler works; I was going to give some code, but actually copying that would be better. Also note that Views has been seeing some changes to $query->add_orderby in 1.2 and they'll affect what you're doing because you're using a formula. Be sure to use the latest.
Comment #2
jpsalter commentedmerlinofchaos - thanks for the tip and links to the new documentation.
I didn't explain myself too well. I'll need to do two things with my new number profile field:
1) filter in views by greater than / less than
2) sort in views by number value
Both of these goals get tripped up by the fact the profile.module is storing the number as text in the database. I've solved #1 with the following code:
The sort code is still giving me problems. I've read the documentation but I must be missing this point. Here is my sort function:
Any pointer or suggestions would be greatly appreciated.
Thanks,
Jason
Comment #3
merlinofchaos commentedEr, 'value' in your code needs to be a more concrete database table + field name.
To start with, you want something in the form of:
That's a much cleaner way to add your orderby. Of course, you then need to replace rand() with basically $table.$field.
Comment #4
jpsalter commentedThank you for putting me on the right track. I think I'm close, but the views sorting still doesn't work for me. Here are the settings I have:
For the array:
and for the functions:
Thanks again for all the help. If this works - this could be used as a helper function for those who need to treat a textfield as a number.
Comment #5
catch4.7 no longer supported for feature requests.
Comment #6
memtkmcc commentedThere is VERY easy workaround for this issue.
You can convert on the fly any value as with CAST AS DECIMAL, using Views Calc module.
1. Create new Views Calc field, for example: ROUND(%Profile:PostCount), where original Profile:PostCount is a plain text value, and choose the format of the result of this calculation to be integer/decimal.
2. Now add new calculated field to your view, but keep there also original Profile:PostCount field.
3. Use any template trick to hide the original Profile:PostCount, so it will be still availabe to "calculation", but not displayed as redundant data.
Thats all. Tested and works for me.
Comment #7
zapscribbles commentedI am using Drupal 6 and tried emaylx's solution. Unfortunately it did not work as it requires the Usernode module (http://drupal.org/project/usernode) to make the Views Calc field available to a User View (by default it is only exposed to a Node View), but currently there is no version of Usernode for Drupal 6.
My only solution to get around this was to create a View listing all posts by user, and requiring the user name as an argument. I set it so that when the argument is missing, a summary is displayed. This resulted in a list of user's with numbers next to them indicating how many posts belong to them.
The only issue with this is that it is ordered by user name, not by the summary number (i.e. the total posts for each user). Does anyone know of a way to do this in the View?
Comment #8
hozt commentedI faced this same problem and was able to solve it first by padding the number with leading zeros with lpad in mysql. This way the sort would work correctly. Next I added the module view_custom. This allows you to use php to format the output of the field. like
<?=intval($data->points)?>In the settings for the custom field check the box to prevent it from showing up. Then add your text field that has the number you want after the custom field and use the rewrite field option to display the value from your custom field. This enabled the sort and display to work perfect in Drupal 6.
Hope this can help!
Comment #9
barber75 commentedHey,
Trying to implement what you did...I can get the value to display in my view, but when using my filter it still treats the fields as text and not an integer or number.
Did you get this working with filters? Your're help would be much appreciated, really panicking on this now, might have to completely rebuild a lot of my site, which is going live in like a day!
cheers
Craig