Creating custom Jamroom Ranking System modes
One of the most powerful features in Jamroom is the Jamroom Ranking System. The Jamroom Ranking System allows you to search, display and retrieve data from your Jamroom in almost any way you would like - it's at the heart of how many of Jamrooms skins and themes get their data. With the module API, Jamroom has a way for us to provide new "modes" for the Jamroom Ranking System, allowing you to have the same flexibility with your module that you do with any of the "modes" that are part of the Jamroom Core.
Due to the number of different options the Jamroom Ranking System supports, this will likely be the most complicated part of your Jamroom module - however, Jamroom takes care of a tremendous amount of the work "behind the scenes", so it is not as hard as it may seem on the surface.
First, let's check out the Jamroom Ranking System plugin as it is in its entirety - we will then cover each section of the plugin below so we're sure what each part does:
<?php
/**
* Jamroom jrYouTube module ranking plugin
* @copyright 2009 by Talldude Networks LLC
* @author Brian Johnson - bigguy@jamroom.net
*/
defined('IN_JAMROOM') or exit();
/**
* The "verify" function validates parameters specific to this type
* @param array Input arguments ($_post)
* @return array Returns modified $_post
*/
function jrRanking_jrYouTube_verify($_args)
{
if (!is_numeric($_args['order']) || ($_args['order'] < 1 || $_args['order'] > 3)) {
$_args['order'] = 1;
}
return($_args);
}
/**
* The "add_url" function validates any URL parameters
* @param array Input arguments ($_post)
* @return array Returns array of allowed URL params
*/
function jrRanking_jrYouTube_add_url(&$_args)
{
$_tmp = array();
// YouTube Video Category
if (isset($_args['category'])) {
$_tmp['category'] = stripHtml(trim($_args['category']));
}
return($_tmp);
}
/**
* The "search_area" defines allowed search_areas in our table
* @param array Input arguments ($_post)
* @return array Returns array of allowed search columns
*/
function jrRanking_jrYouTube_search_areas(&$_args)
{
global $jamroom_db;
// Return the allowed columns for searching
// column_name => column SQL prefix
$_out = array(
'youtube_video_id' => 'y',
'__JR_CST_ALIAS' => 'y',
'__JR_CST_TABLE' => $jamroom_db['jrYouTubeVideos']
);
return($_out);
}
/**
* The "get_random" function retrieves random id's
* @param int Number of random results to retrieve
* @return string Returns string on success
*/
function jrRanking_jrYouTube_get_random($count,$extra_join,$extra_args)
{
global $jamroom_db;
$_rs = jrRankGetRandom('jrYouTube',$count,$extra_join,$extra_args);
if (!isset($_rs) || strlen($_rs) === 0) {
// Looks like we need to create our results
$req = "SELECT youtube_id
FROM {$jamroom_db['jrYouTubeVideos']} y
LEFT JOIN {$jamroom_db['band_info']} i ON i.band_id = y.youtube_band_id
LEFT JOIN {$jamroom_db['quota']} q ON q.quota_id = i.band_quota {$extra_join}
WHERE i.band_active = '1'
AND q.quota_rank != 'no' {$extra_args} ";
$_bi = dbQuery($req,'youtube_id');
$_rs = jrRankSaveRandom('jrYouTube',$_bi,$count,$extra_join,$extra_args);
}
return($_rs);
}
/**
* The "query" function is used for constructing and processing the SQL query
* @param array Input arguments ($_post)
* @param string Additional SQL constructed for search
* @param string Unique Cache Key constructed for caching
* @return array returns an array of data for processing
*/
function jrRanking_jrYouTube_query(&$_args,$search_sql = null,$ckey = null)
{
global $jamroom, $jamroom_db, $config;
$add = '';
// YouTube_ID
$add .= jrRankAddQuery('y.youtube_id','youtube_id',$_args);
// YouTube Category
$add .= jrRankAddQuery('y.youtube_video_category','category',$_args);
// Add in our search SQL
if (!is_null($search_sql)) {
$add .= ' '. $search_sql;
}
// Counting Query - need full count if doing pages
if (is_numeric($_args['pagebreak'])) {
$req = "SELECT count(1) AS num
FROM {$jamroom_db['jrYouTubeVideos']} y
LEFT JOIN {$jamroom_db['band_info']} i ON i.band_id = y.youtube_band_id
LEFT JOIN {$jamroom_db['quota']} q ON q.quota_id = i.band_quota
WHERE i.band_active = '1'
AND q.quota_rank != 'no' {$add} ";
$GLOBALS['JR_RANK_COUNT_QUERY'][$ckey] = dbQuery($req,'SINGLE');
}
// BUILD FULL QUERY
$method = 'm2';
if (isset($config['rating_method']) && $config['rating_method'] == 'band') {
$method = 'm1';
}
$req = "SELECT y.*,
i.*,
i.band_{$method}_rating_1 AS band_rating_1,
i.band_{$method}_rating_2 AS band_rating_2,
i.band_{$method}_rating_3 AS band_rating_3,
i.band_{$method}_rating_4 AS band_rating_4,
i.band_{$method}_rating_5 AS band_rating_5,
i.band_{$method}_rating_number AS band_rating_number,
i.band_{$method}_rating_average AS band_rating_average,
q.*
FROM {$jamroom_db['jrYouTubeVideos']} y
LEFT JOIN {$jamroom_db['band_info']} i ON i.band_id = y.youtube_band_id
LEFT JOIN {$jamroom_db['quota']} q ON q.quota_id = i.band_quota
WHERE i.band_active = '1'
AND q.quota_rank != 'no' {$add} ";
// Check how we are ordering
switch ($_args['order']) {
case '1': $req .= "ORDER BY y.youtube_id DESC "; break;
case '2': $req .= "ORDER BY y.youtube_id ASC "; break;
case '3':
$num = $config['number_limit'];
if (checkType($_args['show'],'number_nz')) {
$num = $_args['show'];
}
elseif (checkType($_args['pagebreak'],'number_nz')) {
$num = $_args['pagebreak'];
}
$tmp = jrRanking_jrYouTube_get_random($num,'',$add,$_args);
$req .= "AND y.youtube_id IN({$tmp}) ORDER BY FIELD(y.youtube_id,{$tmp}) ";
break;
default:
// Check for an "order_by" clause that would come in from a search
if (isset($_args['order_by'])) {
switch ($_args['order_dir']) {
case 'NUMERICAL_ASC':
$req .= "ORDER BY ({$_args['order_by']} + 0) ASC ";
break;
case 'NUMERICAL_DESC':
$req .= "ORDER BY ({$_args['order_by']} + 0) DESC ";
break;
default:
$req .= "ORDER BY {$_args['order_by']} {$_args['order_dir']} ";
break;
}
}
else {
$req .= "ORDER BY y.youtube_time DESC ";
}
break;
}
if (!isset($tmp)) {
$req .= jrRankGetLimit($_args);
}
$_rt = dbQuery($req,'NUMERIC');
return($_rt);
}
/**
* The "process" function is used to process the output variables
* @param array Results array from output of query function
* @param array Parameters as passed in ($_post)
* @param string 32bit unique ranking key based on parameters
* @return string echos HTML from processed row template
*/
function jrRanking_jrYouTube_process(&$_results,&$_args,$key)
{
global $config, $jamroom, $_user, $jamroom_db, $cluster;
if (!is_array($_results)) {
return(false);
}
// Get our template information
$_tpl = htmlGetTemplate('row_template',$_args,'ranking','jr_ranking_row.tpl');
// How many results do we have
$fnum = ($_args['begin_number'] + 1);
$lnum = (($_args['pagenum'] - 1) * $_args['pagebreak']) + count($_results);
ob_start();
$rnum = $_args['begin_number'];
foreach ($_results as $_row) {
$_rep = array_change_key_case($_row,CASE_UPPER);
$_rep['YOUTUBE_VIDEO_RANK'] = ++$rnum;
$_rep['RANK_NUM'] = $_rep['YOUTUBE_VIDEO_RANK'];
$_rep['FIRST_RANK_NUM'] = $fnum;
$_rep['LAST_RANK_NUM'] = $lnum;
$_rep = jrArrayMerge($GLOBALS['JR_RANKING'][$key],$_rep);
htmlShowTemplate($_tpl['tpl_dir'],$_tpl['template'],$_rep,$_tpl['theme']);
}
$out = ob_get_contents();
ob_end_clean();
return($out);
}
?>
As you can see this is our largest PHP script so far - it has several different functions within that all play a role in allowing the Jamroom user the most flexibility possible.
Let's start at the top and break it down into sections:
Header
/**
* Jamroom jrYouTube module ranking plugin
* @copyright 2009 by Talldude Networks LLC
* @author Brian Johnson - bigguy@jamroom.net
*/
defined('IN_JAMROOM') or exit();
Just like in every other PHP script that is part of our Module, we include our comment header and "IN_JAMROOM" check - don't leave this out as it is required.
Verify Function
/**
* The "verify" function validates parameters specific to this type
* @param array Input arguments ($_post)
* @return array Returns modified $_post
*/
function jrRanking_jrYouTube_verify($_args)
{
if (!is_numeric($_args['order']) || ($_args['order'] < 1 || $_args['order'] > 3)) {
$_args['order'] = 1;
}
return($_args);
}
The Verify Function receives as its only parameter the $_args array - this is the data as has come in from either the {jr_ranking} Template function, or posted via ranking.php - this will contain key => value pairs of all of the different parameters that were passed in (i.e. pagebreak, order, etc.). Jamroom will pass this data to your Verify function, where you will need to verify any parameters that are specific to your module! In our case, the only parameter that is specific to our module is the "order" - we need to be sure it is set to either 1, 2 or 3 - since that is the only order values we accept. Note that if no order is received, we set a default (1).
Add URL Function
/**
* The "add_url" function validates any URL parameters
* @param array Input arguments ($_post)
* @return array Returns array of allowed URL params
*/
function jrRanking_jrYouTube_add_url(&$_args)
{
$_tmp = array();
// YouTube Video Category
if (isset($_args['category'])) {
$_tmp['category'] = stripHtml(trim($_args['category']));
}
return($_tmp);
}
Next up we have the "Add URL Function" - this function is used to include any parameters that need to be included on previous/next link URLs - that way our previous page and next page links contain the correct parameters as passed in. For our YouTube module we are only going to check for the "category" parameter that may have been passed in - if it was, we check it, strip any HTML from it, then add it to an array that we then return out of the function - Jamroom will take it from there and ensure the parameter is inserted properly into prev/next page URLs.
Search Areas Function
/**
* The "search_area" defines allowed search_areas in our table
* @param array Input arguments ($_post)
* @return array Returns array of allowed search columns
*/
function jrRanking_jrYouTube_search_areas(&$_args)
{
global $jamroom_db;
// Return the allowed columns for searching
// column_name => column SQL prefix
$_out = array(
'youtube_video_id' => 'y'
'__JR_CST_ALIAS' => 'y',
'__JR_CST_TABLE' => $jamroom_db['jrYouTubeVideos']
);
return($_out);
}
Next we have the "Search Areas" function. This function allows us to define what database columns in our database table will be searchable. If the user passes in a set of search_area parameters, then Jamroom will test those search_areas given and ensure they are part of the module table and are allowed.
Note that we have 2 "special" entries in the array that we return:
- __JR_CST_ALIAS - this MUST be set to the "alias" you use in the query function - this allows Jamroom to use the proper table alias when constructing a search query.
- __JR_CST_TABLE - this MUST be set to the Jamroom database table you are using for your module - this ensures Jamroom references the proper database table when constructing a search query.
Get Random Function
/**
* The "get_random" function retrieves random id's
* @param int Number of random results to retrieve
* @return string Returns string on success
*/
function jrRanking_jrYouTube_get_random($count,$extra_join,$extra_args)
{
global $jamroom_db;
$_rs = jrRankGetRandom('jrYouTube',$count,$extra_join,$extra_args);
if (!isset($_rs) || strlen($_rs) === 0) {
// Looks like we need to create our results
$req = "SELECT youtube_id
FROM {$jamroom_db['jrYouTubeVideos']} y
LEFT JOIN {$jamroom_db['band_info']} i ON i.band_id = y.youtube_band_id
LEFT JOIN {$jamroom_db['quota']} q ON q.quota_id = i.band_quota {$extra_join}
WHERE i.band_active = '1'
AND q.quota_rank != 'no' {$extra_args} ";
$_bi = dbQuery($req,'youtube_id');
$_rs = jrRankSaveRandom('jrYouTube',$_bi,$count,$extra_join,$extra_args);
}
return($_rs);
}
The "Get Random" function is a special (optional) function within the Ranking System, and does not need to be used - we could have left this function out to make the plugin simpler, but it's a powerful function and covering it will allow you to understand why it may (or may not) be needed in your Ranking System plugin.
If you are familiar with MySQL functions, you know there is a RAND() function that will return a random row from a table - however, as a dataset grows, and you have tens of thousands (or hundreds of thousands or more) entries in a single table, the MySQL RAND() function becomes extremely slow. This is where Jamroom's Get Random function steps in by "pre-creating" randomness in a special "random database" table. This allows the random results to be retrieved VERY fast, even on large datasets.
Query Function
/**
* The "query" function is used for constructing and processing the SQL query
* @param array Input arguments ($_post)
* @param string Additional SQL constructed for search
* @param string Unique Cache Key constructed for caching
* @return array returns an array of data for processing
*/
function jrRanking_jrYouTube_query(&$_args,$search_sql = null,$ckey = null)
{
global $jamroom, $jamroom_db, $config;
$add = '';
// YouTube_ID
$add .= jrRankAddQuery('y.youtube_id','youtube_id',$_args);
// YouTube Category
$add .= jrRankAddQuery('y.youtube_video_category','category',$_args);
// Add in our search SQL
if (!is_null($search_sql)) {
$add .= ' '. $search_sql;
}
// Counting Query - need full count if doing pages
if (is_numeric($_args['pagebreak'])) {
$req = "SELECT count(1) AS num
FROM {$jamroom_db['jrYouTubeVideos']} y
LEFT JOIN {$jamroom_db['band_info']} i ON i.band_id = y.youtube_band_id
LEFT JOIN {$jamroom_db['quota']} q ON q.quota_id = i.band_quota
WHERE i.band_active = '1'
AND q.quota_rank != 'no' {$add} ";
$GLOBALS['JR_RANK_COUNT_QUERY'][$ckey] = dbQuery($req,'SINGLE');
}
// BUILD FULL QUERY
$method = 'm2';
if (isset($config['rating_method']) && $config['rating_method'] == 'band') {
$method = 'm1';
}
$req = "SELECT y.*,
i.*,
i.band_{$method}_rating_1 AS band_rating_1,
i.band_{$method}_rating_2 AS band_rating_2,
i.band_{$method}_rating_3 AS band_rating_3,
i.band_{$method}_rating_4 AS band_rating_4,
i.band_{$method}_rating_5 AS band_rating_5,
i.band_{$method}_rating_number AS band_rating_number,
i.band_{$method}_rating_average AS band_rating_average,
q.*
FROM {$jamroom_db['jrYouTubeVideos']} y
LEFT JOIN {$jamroom_db['band_info']} i ON i.band_id = y.youtube_band_id
LEFT JOIN {$jamroom_db['quota']} q ON q.quota_id = i.band_quota
WHERE i.band_active = '1'
AND q.quota_rank != 'no' {$add} ";
// Check how we are ordering
switch ($_args['order']) {
case '1': $req .= "ORDER BY y.youtube_id DESC "; break;
case '2': $req .= "ORDER BY y.youtube_id ASC "; break;
case '3':
$num = $config['number_limit'];
if (checkType($_args['show'],'number_nz')) {
$num = $_args['show'];
}
elseif (checkType($_args['pagebreak'],'number_nz')) {
$num = $_args['pagebreak'];
}
$tmp = jrRanking_jrYouTube_get_random($num,'',$add,$_args);
$req .= "AND y.youtube_id IN({$tmp}) ORDER BY FIELD(y.youtube_id,{$tmp}) ";
break;
default:
// Check for an "order_by" clause that would come in from a search
if (isset($_args['order_by'])) {
switch ($_args['order_dir']) {
case 'NUMERICAL_ASC':
$req .= "ORDER BY ({$_args['order_by']} + 0) ASC ";
break;
case 'NUMERICAL_DESC':
$req .= "ORDER BY ({$_args['order_by']} + 0) DESC ";
break;
default:
$req .= "ORDER BY {$_args['order_by']} {$_args['order_dir']} ";
break;
}
}
else {
$req .= "ORDER BY y.youtube_time DESC ";
}
break;
}
if (!isset($tmp)) {
$req .= jrRankGetLimit($_args);
}
$_rt = dbQuery($req,'NUMERIC');
return($_rt);
}
You can see that this is main "workhorse" function of the plugin - it constructs the actual SQL query that will be used to get the data requested. Note the use of the following:
- Using the jrRankAddQuery() function to "add" in a potential parameter to the SQL query (ongoing $add variable). The jrRankAddQuery function handles the data in several formats, so make sure and use it for your parameters.
- We check for the "pagebreak" variable before running our "counting" query - we only need to run the counting query if there is the potential for MORE then 1 page - if a "show" parameter was used, the pagebreak will be empty and we can save running a SQL query.
- We check the "rating_method" $config setting to see what type of profile ratings we are doing - method 1 or method 2. This ensure we return the proper database column from the band_info table depending on how the Jamroom owner has their rating settings.
Process Function
/**
* The "process" function is used to process the output variables
* @param array Results array from output of query function
* @param array Parameters as passed in ($_post)
* @param string 32bit unique ranking key based on parameters
* @return string echos HTML from processed row template
*/
function jrRanking_jrYouTube_process(&$_results,&$_args,$key)
{
global $config, $jamroom, $_user, $jamroom_db, $cluster;
if (!is_array($_results)) {
return(false);
}
// Get our template information
$_tpl = htmlGetTemplate('row_template',$_args,'ranking','jr_ranking_row.tpl');
// How many results do we have
$fnum = ($_args['begin_number'] + 1);
$lnum = (($_args['pagenum'] - 1) * $_args['pagebreak']) + count($_results);
ob_start();
$rnum = $_args['begin_number'];
foreach ($_results as $_row) {
$_rep = array_change_key_case($_row,CASE_UPPER);
$_rep['YOUTUBE_VIDEO_RANK'] = ++$rnum;
$_rep['RANK_NUM'] = $_rep['YOUTUBE_VIDEO_RANK'];
$_rep['FIRST_RANK_NUM'] = $fnum;
$_rep['LAST_RANK_NUM'] = $lnum;
$_rep = jrArrayMerge($GLOBALS['JR_RANKING'][$key],$_rep);
htmlShowTemplate($_tpl['tpl_dir'],$_tpl['template'],$_rep,$_tpl['theme']);
}
$out = ob_get_contents();
ob_end_clean();
return($out);
}
The process function is the last function in our plugin - this function is responsible for processing the rows of data that were retrieved from the database and outputting the results via the htmlShowTemplate function. It is within this function that you can add in any additionl "row" template variables that you might want to construct from the data results.
Some things to note about the Process Function:
- We use the htmlGetTemplate() function to make sure all of the proper "template" parameters (template, tpl_dir, skin, theme) are set properly based on the parameters received.
- We use ob_start() and ob_end_clean() around the actual "output" of the template via htmlShowTemplate(). We DO NOT want to echo the results out of our function - instead, we need to "capture" the output and return it from the function. This allows the main Jamroom Ranking System engine to apply any highlighting as necessary.
|