JElement: The interface for Parameter of a View
There is one more piece of the puzzle! That is about "ltable" view. Its template's metadata file:
<?xml version="1.0" encoding="utf-8"?>
<metadata>
<layout title="Season Table Layout">
<message>
<![CDATA[Season Table Layout]]>
</message>
</layout>
<state>
<name>Season Table Layout</name>
<description>Season Table Layout</description>
<url addpath="/administrator/components/com_joomsport/elements">
<param name="sid" type="season" default="0" label="Select Season" description="Season" />
<param name="gr_id" type="season" default="0" label="Select Group" description="Group" />
</url>
<params>
</params>
</state>
</metadata>
There are two 'param' nodes inside 'url' node, and they have the same value for 'type' attribute: "season". As explored in previous days, this description will lead Joomla! to load the 'season.php' under '/administrator/components/com_joomsport/elements/', and the question is why to load single 'season' element for both the two parameters? Now take a look at what does 'season.php' do on the neath:
<?php
defined('_JEXEC') or die( 'Restricted access' );
class JElementSeason extends JElement
{
/**
* Element name
*
* @access protected
* @var string
*/
var $_name = 'Season';
function fetchElement($name, $value, &$node, $control_name)
{
//echo 'reach here, in fetchElement()';
global $mainframe;
$db =& JFactory::getDBO();
$doc =& JFactory::getDocument();
$template = $mainframe->getTemplate();
$fieldName = $control_name.'['.$name.']';
$article->title = '';
//dump($name, 'name');
//dump($value, 'value');
//dump($node, 'node');
//dump($control_name, 'control_name');
if($name == 'sid')
{
if ($value)
{
//echo 'reach here, in fetchElement, value is valid';
$query = "SELECT CONCAT(t.name,' ',s.s_name) as name FROM #__bl_tournament as t, #__bl_seasons as s WHERE s.t_id = t.id AND s.s_id = ".$value;
$db->setQuery($query);
$rows = $db->loadObjectList();
if(isset($rows[0]))
{
$row = $rows[0];
$article->title = $row->name;
}
}
else
{
echo 'reach here, in fetchElement, value is invalid';
$article->title = JText::_('Select Season');
}
$task = 'season_menu';
}
else
{
if ($value)
{
$query = "SELECT group_name FROM #__bl_groups WHERE id = ".$value;
$db->setQuery($query);
$rows = $db->loadObjectList();
if(isset($rows[0]))
{
$row = $rows[0];
$article->title = $row->group_name;
}
}
else
{
$article->title = JText::_('Select Group');
}
$task = 'group_menu';
}
$js = "
function jSelectArticle(id, title, object) " .
"{
document.getElementById(object + '_id').value = id;
document.getElementById(object + '_name').value = title;
document.getElementById('sbox-window').close();
}";
$doc->addScriptDeclaration($js);
$link = 'index.php?option=com_joomsport&task='.$task.'&tmpl=component&object='.$name;
JHTML::_('behavior.modal', 'a.modal');
$html = "\n".'<div style="float: left;"><input style="background: #ffffff;" type="text" id="'.$name.'_name" value="'.htmlspecialchars($article->title, ENT_QUOTES, 'UTF-8').'" disabled="disabled" /></div>';
$html .= '<div class="button2-left"><div class="blank"><a class="modal" title="'.$article->title.'" href="'.$link.'" rel="{handler: \'iframe\', size: {x: 650, y: 375}}">'.JText::_('Select').'</a></div></div>'."\n";
$html .= "\n".'<input type="hidden" id="'.$name.'_id" name="'.$fieldName.'" value="'.(int)$value.'" />';
return $html;
}
}
In fact it define one class 'JElementSeason' which extends 'JElement' in Joomla! framework, and this class have one method 'fetchElement'
According to the body of 'fetchElement', its main job is to generate the HTML code for the 'Element' of a menu type, namely the UI for parameters of a view. The two 'param' nodes inmetadata.xml cause Joomla! to call 'fetchElement' twice, and generate the two parameters' setting interface(please note: the generated HTML doesn't include the popup data list for selecting):
Then method 'fetchElement' should be able to discriminate the two conditions, and in fact it does be, it does that by checking '$name' value. And what are the values passed to this method? Put 'dump' inside it to see:
dump($name, 'name');
dump($value, 'value');
dump($node, 'node');
dump($control_name, 'control_name');
The var '$node' is never referenced, and '$control_name' is a fixed string for these cases, so we can igorne them. Then the relation between the method 'fetchElement' and the 'metadata.xml' is:
Inside 'fetchElement', '$value' is used as season id for database query():
if ($value)
{
$query = "SELECT CONCAT(t.name,' ',s.s_name) as name FROM #__bl_tournament as t, #__bl_seasons as s WHERE s.t_id = t.id AND s.s_id = ".$value;
$db->setQuery($query);
......
}
else
{
$article->title = JText::_('Select Season');
}
Please note: these queries are only for determining the text to display in the text field("主席碟 2011-2012", for example). But because passed value for argument '$value' is always '0', the control flow will never reach the if block, it will always reach else block instead, so you will see "Select Season" instruction instead, for your first time specifying that type for a menu. This is a trick, since in MySQL, 1 will be used as seed for auto-augument field, so '0' is used for indicating no record specified.
The two links for getting the data list generated by this method is:
for sid:
http://localhost/soccer/administrator/index.php?option=com_joomsport&task=season_menu&tmpl=component&object=sid
for gr_id:
http://localhost/soccer/administrator/index.php?option=com_joomsport&task=group_menu&tmpl=component&object=gr_id
The relevent code is:
$link = 'index.php?option=com_joomsport&task='.$task.'&tmpl=component&object='.$name;
Back End Controller: predefining the tasks for popup the data list
admin.joomsport.php:
function BL_Season_Menu($option)
{
$db =& JFactory::getDBO();
$query = "SELECT s.s_id as id, CONCAT(t.name,' ',s.s_name) as name FROM #__bl_tournament as t, #__bl_seasons as s WHERE s.published = '1' AND t.published = '1' AND s.t_id = t.id AND s.t_id = t.id ORDER BY t.name, s.s_name";
$db->setQuery($query);
$row = $db->loadObjectList();
joomsport_html::bl_SeasonMenu($row, $option);
}
function BL_Group_Menu($option)
{
$mainframe = JFactory::getApplication();
$limit = $mainframe->getUserStateFromRequest( 'global.list.limit', 'limit', $mainframe->getCfg('list_limit'), 'int' );
$limitstart = $mainframe->getUserStateFromRequest( $option.'.limitstart', 'limitstart', 0, 'int' );
$db =& JFactory::getDBO();
$query = "SELECT COUNT(*) FROM #__bl_groups as g, #__bl_tournament as t, #__bl_seasons as s WHERE s.t_id = t.id AND s.s_id=g.s_id";
$db->setQuery($query);
$total = $db->loadResult();
jimport('joomla.html.pagination');
$pageNav = new JPagination( $total, $limitstart, $limit );
$query = "SELECT g.*,CONCAT(t.name,' ',s.s_name) as name FROM #__bl_groups as g, #__bl_tournament as t, #__bl_seasons as s WHERE s.t_id = t.id AND s.s_id=g.s_id ORDER BY g.group_name";
$db->setQuery($query, $pageNav->limitstart, $pageNav->limit);
$rows = $db->loadObjectList();
joomsport_html::bl_GroupMenu($rows, $option);
}
switch (JRequest::getCmd('task'))
{
......
//---menu-------------//
case 'season_menu': BL_Season_Menu($option); break;
......
case 'group_menu': BL_Group_Menu($option); break;
......
}
So, to sum up, JElement class its responsiblity is to generate the HTML user control for parameters of view, to let user to select value for that parameter, it also generate a link to the component to generate the popup data list, and component's controller should take care of generating the list. Each parameter panel has a hidden input field, which holds the value for that parameter:
The line to generate the input field is:
$html .= "\n".'<input type="hidden" id="'.$name.'_id" name="'.$fieldName.'" value="'.(int)$value.'" />';
And the js code for updating its value:
$js = "
function jSelectArticle(id, title, object) " .
"{
document.getElementById(object + '_id').value = id;
document.getElementById(object + '_name').value = title;
document.getElementById('sbox-window').close();
}";