Skip to content

Instantly share code, notes, and snippets.

@jlainezs
Last active December 12, 2015 03:18
Show Gist options
  • Save jlainezs/4705905 to your computer and use it in GitHub Desktop.
Save jlainezs/4705905 to your computer and use it in GitHub Desktop.
Joomla! DBFormFields are JFormField descendants which fetch data from the database. The inspiration come from the classic master-detail implementation which I've coded twice for a project of mine. As I don't like to repeat code I prefer to implement this JFormField descendant classes to improve the project maintenance.
<?php
/**
* Copyright (C) 2013 Pep Lainez
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @copyright 2013, Pep Lainez
*/
defined('JPATH_PLATFORM') or die;
/**
* Provides basic infrastructure for fields which fetch data from the database
* As abstract class it cannot be instantiate directly.
* @author Pep Lainez
* @package JFormFieldDBFields
* @since 1.0
* @see http://docs.joomla.org/Creating_a_custom_form_field_type
*/
abstract class JFormFieldDBField extends JFormField{
/**
* Gets the query and replaces parameters
* using JRequest variables
*/
protected function prepareQuery($query){
$regEx = "/@[a-zA-Z0-9]*/ims";
preg_match_all($regEx, $query, $result, PREG_PATTERN_ORDER);
if (count($result)>1){
$values = array();
if (count($result[1])){
foreach($result[1] as $var){
$values[$var] = JRequest::getString($var,'');
}
}
if (count($values)){
foreach($values as $var => $value){
$query = str_replace($var, $value, $query);
}
}
}
return $query;
}
/**
* If element is not set creates a new one
* (usefull for programatically access)
*/
private function initElement(){
if (!isset($this->element))
$this->element = new SimpleXMLElement('<field></field>');
}
/**
* Adds the columns as children of the element
* @param Array $columns
*/
public function setColumns($columns){
$this->initElement();
foreach($columns as $column){
$newCol = $this->element->addChild('column');
foreach($column as $key=>$value){
$newCol->addAttribute($key, $value);
}
}
}
/**
* Published SimpleXMLElement addAttribute method
* @param String $key Name of the attribute
* @param String $value Value of the attribute
* @see http://www.php.net/manual/es/simplexmlelement.addattribute.php
*/
public function addAttribute($key, $value){
$this->initElement();
$this->element->addAttribute($key, $value);
}
private $rows;
/**
* Executes the query
*/
public function setRows($rowsToLoad){
$this->rows = $rowsToLoad;
}
public function getRows(){
return $this->rows;
}
protected function loadData(){
$q = $this->element['query'];
if ($q != ''){
$this->rows = null;
$prepared = $this->prepareQuery($q);
$dbo =& JFactory::getDBO();
$query = $dbo->getQuery(true);
$dbo->setQuery($prepared);
$this->rows = $dbo->loadObjectList();
}
}
/**
* Configures default columns using the data source
* @param Object $record
*/
protected function configureColumns($record){
$aColumns = array();
$properties = get_object_vars($record);
foreach($properties as $key=>$value){
$aColumns[$key] = array('title' => $value);
}
$this->setElement('columns', json_encode($aColumns));
}
}
<?php
/**
* Copyright (C) 2013 Pep Lainez
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
defined('JPATH_PLATFORM') or die;
require_once JPATH_COMPONENT_ADMINISTRATOR.'/models/fields/dbfield.php';
/**
* Renders an HTML table fetching data from a SQL sentence
* The SQL sentence can be parametrized using @var1 ... @varn where varX is the name of a get or post variable.
*
* XML form definition syntax:
* <field type="DetailTable"
* sql = "SQL_STATEMENT"
* id = "<an id for the table>"
* label = "<a text for the label>"
* class = "<class name>"
* style = "<CSS inline style>"
* >
* <column title="<column heading>" field="<field to show>" class="<cell style>" style="<CSS inline style>" />
* <column title="<column heading>" field="<field to show>" class="<cell style>" style="<CSS inline style>" />
* ...
* </field>
*
* This field type could be used programmatically too
*
* @author Pep Lainez
* @package JFormFieldDBFields
* @since 1.0
* @see http://docs.joomla.org/Creating_a_custom_form_field_type
*/
class JFormFieldDetailTable extends JFormFieldDBField{
protected $type = "DetailTable";
/**
* Prepares the table header
* @param Array $columnsDefinition Definition of the table columns
* @return string Header of the table
*/
private function getTableHeader(){
$result = "<thead>";
$result .= "<tr>";
foreach ($this->element->children() as $column){
$class = isset($column['class']) ? ' class="'.$column['class'].'"' : '';
$style = isset($column['style']) ? ' style="'.$column['style'].'"' : '';
$result .= sprintf("<th%s%s>%s</th>",$class, $style, JText::_($column['title']) );
}
$result .= "</tr>";
$result .= "</thead>";
return $result;
}
/**
* Builds a row of the table body
* @param Object $record
* @return String
*/
protected function getBodyRow($record){
$result = "";
foreach($this->element->children() as $column){
$class = ( isset($column['class'] ) ? ' class="'.$column['class'].'"' : '' );
$style = ( isset($column['style'] ) ? ' style="'.$column['style'].'"' : '' );
$result .= sprintf("<td%s%s>%s</td>", $class, $style, $record->$column['field']);
}
return $result;
}
/**
* Prepares the table body
* @param unknown $records Records to show
* @param unknown $colsDefs Columns definitions
*/
private function getTableBody($records){
$result = "";
$i=0;
foreach($records as $record){
$result .= "<tr>";
$result .= $this->getBodyRow($record);
$result .= "</tr>";
$i++;
}
return $result;
}
/**
* Prepares the table footer
*/
private function getTableFooter(){
return sprintf("<tfoot><tr><td colspan=\"%s\"></td></tr></tfoot>", $this->element->count());
}
/**
* Builds an HTML table
*/
public function getInput(){
$html = array();
$class = ( isset( $this->element['class'] ) ? ' class="'.$this->element['class'].'"' : '' );
$style = ( isset( $this->element['style'] ) ? ' style="'.$this->element['style'].'"' : '' );
$records = $this->loadData();
$html[] = sprintf("<table id=\"%s\"%s%s>", $this->element['id'], $class, $style );
$html[] = $this->getTableHeader();
$html[] = $this->getTableBody($records);
$html[] = $this->getTableFooter();
$html[] = '</table>';
return implode("",$html);
}
}
<?php
/**
* @author jlainezs
* @package dbfields
* @subpackage
* @license Licensed under GNU/GPL 3.0
* @since 1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
defined('JPATH_PLATFORM') or die;
require_once JPATH_COMPONENT_ADMINISTRATOR.'/models/fields/dbfield.php';
/**
* Renders a combobox
* @author jlainezs
* @see http://docs.joomla.org/Creating_a_custom_form_field_type
*/
class JFormFieldDropdownDb extends JFormFieldDBField{
protected $type = "DropdownDb";
/**
* Builds the options of the dropdown
* @param Array $records
* @return String html options list
*/
protected function getDdOptions($records){
$options = "<option value=\"\">**</option>";
$fkeyfield = $this->element['key'];
$flabelField = $this->element['displayvalue'];
if (count($records)){
foreach($records as $record){
$value = $record->$fkeyfield;
$optLabel = $record->$flabelField;
$selected = ( $this->value == $value ? " selected=\"selected\"" : "" );
$options .= sprintf("<option value=\"%s\"%s>%s</option>",$value, $selected, $optLabel);
}
}
return $options;
}
/**
* Renders a SELECT tag
* @return String
*/
public function getInput(){
$html = array();
$class = ( isset( $this->element['class'] ) ? ' class="'.$this->element['class'].'"' : '' );
$style = ( isset( $this->element['style'] ) ? ' style="'.$this->element['style'].'"' : '' );
//$name = ( isset( $this->element['name'] ) ? ' name="'.$this->element['name'].'"' : '' );
//$id = ( isset( $this->element['id'] ) ? ' id="'.$this->element['id'].'"' : '' );
$records = $this->loadData();
$html[] = sprintf("<select id=\"%s\" name=\"%s\"%s%s>", $this->id, $this->name, $class, $style );
$html[] = $this->getDdOptions($records);
$html[] = '</select>';
return implode("",$html);
}
}
<?php
/**
* @author jlainezs
* @package dbfields
* @subpackage
* @license Licensed under GNU/GPL 3.0
* @since 1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
defined('JPATH_PLATFORM') or die;
require_once JPATH_COMPONENT_ADMINISTRATOR.'/models/fields/dbfield.php';
/**
* Renders a table with edit capabilities
* It uses jsTable as a table handling script
* Cell elements are JFormField descendants, so we may
* use any field type *EXCEPT* JFormFieldEdiTable (this is not controlled!)
*
* @author jlainezs
* @see http://docs.joomla.org/Creating_a_custom_form_field_type
* @see http://docs.joomla.org/Retrieving_request_data_using_JInput#Getting_Values_from_a_Specific_Super_Global
* @see http://mootools.net/forge/p/jstable
*
*/
class JFormFieldEdiTable extends JFormFieldDBField{
/**
* Field type name
* @var String
*/
protected $type = "EdiTable";
protected $orginalFieldNames = array();
/**
* Formats the name
* @param String $originalName
*/
protected function normalizeFieldName($originalName){
$normalizedName = sprintf("%s[%s][]", $this->name, $originalName );
return $normalizedName;
}
/**
* Gets the column model
* @return String the column model
*/
public function getColumnModel(){
$columns = array();
$n = 0;
$this->originalFieldNames[] = array();
foreach($this->element->children() as $field){
$attributes = $field->attributes();
$columns[] = new SimpleXMLElement($attributes[0]);
$attributes = $columns[$n]->attributes();
$name = (string)$attributes['name'];
//echo sprintf("%s[%s][]", $this->name, $attributes['name'] )."<br/>";
$attributes['name'] = $this->normalizeFieldName($name);
$this->orginalFieldNames[(string)$attributes['name']] = $name;
$n++;
}
return $columns;
}
/**
* Creates the row action buttons
* @param String $gridId Table identifier
* @return String
*/
protected function createActionButtons($gridId){
return '$e("input",{
"type" : "button",
"value" : "Del",
"events" : {
"click" : function() {
var rwid = this.getParent("tr").id;
var parts = rwid.split("-");
if (parts.length > 0){
var rwndx = parts[parts.length-1];
'.$gridId.'.deleteRowById(parseInt(rwndx));
}
}
}
})';
}
/**
* Creates a cell content
* @param String $gridId Grid identifier
* @param Object $column column definition
* @param String $value initial value
* @return String HTML Tag for cell content
*/
protected function createCellContent($gridId, $column, $value){
$value = htmlentities($value, ENT_QUOTES, 'UTF-8');
$attribs = $column[0]->attributes();
$fld= JFormHelper::loadFieldType($attribs['type'],true);
if (is_object($fld)){
$fld->setup($column, $value);
return $fld->getInput();
}
return "";
}
/**
* Gets the primary key definition
*/
protected function getPKFieldDefinition($value=""){
//$a = array('field'=>$this->getKeyFieldName(), 'dataType'=>'hidden');
$pkfld = new SimpleXMLElement("<field type=\"hidden\" name=\"".$this->getKeyFieldName()."\" value=\"".$value."\" />");
return $this->createCellContent("", $pkfld, $value);
}
/**
* Creates the table columns.
* jsTable requires to create the columns before adding rows.
* @param String $gridId the grid
* @param String $html HTML *REFERENCE* to add the javascript
* @return Array:String columns definitions
*/
protected function createColumns($gridId, &$html){
$cols = $this->getColumnModel();
$newRowDeclaration = array();
$value = "";
foreach($cols as $column){
$newRowDeclaration[] = $this->createCellContent($gridId, $column, $value);
$attribs = $column->attributes();
$html .= " $gridId.addColumn( '".$attribs['label']."' ); ";
}
$newRowDeclaration[0] = $this->getPKFieldDefinition().$newRowDeclaration[0];
return $newRowDeclaration;
}
/**
* Initializes the data of the grid
* @param String $gridId Grid identifier
* @return String Javascript fragment which adds existing rows into the table
*/
protected function initializeTable($gridId){
$columns = $this->getColumnModel();
$deleteButton = $this->createActionButtons($gridId);
if (count($this->getRows())){
foreach($this->getRows() as $row){
$pkcol = $this->getPKFieldDefinition();
$content = array();
foreach($columns as $column){
$attribs = $column->attributes();
$fld = $this->orginalFieldNames[(string)$attribs['name']];
if ($fld != ''){
$content[] = $this->createCellContent($gridId, $column, $row->$fld);
}
}
$fld = $this->getKeyFieldName();
$content[0] = $this->getPKFieldDefinition($row->$fld).$content[0];
$jsContent = "'".implode("','", $content) . "'";
$html .= $gridId.".addRow( $jsContent, $deleteButton);\r\n";
}
}
return $html;
}
/**
* Prepares the table markup
* @param String $gridId Table identifier
*/
protected function createTable($gridId){
$html = "
<script type='text/javascript'>
window.addEvent('load', function(){
var $gridId = new jsTable('$gridId');
";
$newRowDeclaration = $this->createColumns($gridId, $html);
$html .= " $gridId.addColumn('Acciones');";
$deleteButton = $this->createActionButtons($gridId);
$html .= "
document.id(\"addRowBtn-$gridId\")
.addEvent(\"click\", function(){
var clls = new Array();
var fldId = new Date().getTime();
";
$i=0;
$newcellscontent="";
foreach($newRowDeclaration as $rowDec){
$html .= " clls[$i] = '$rowDec';\r\n";
$i++;
$newcellscontent .= ($newcellscontent != '' ? ',' : '').'""';
}
$html .= "
$gridId.addRow( $newcellscontent, $deleteButton );
for(i=0;i<$gridId.columnCount()-1;i++){
$gridId.setCell($gridId.rowCount()-1, i, clls[i]);
}
}); // addEvent
$gridId.setEmptyMessage('No hay datos disponibles.',true);
";
$html .= $this->initializeTable($gridId);
$html .= "
}); // window.addEvent
</script>
<div class=\"toolbar\">
<input type=\"button\" value=\"Añadir entrada\" id=\"addRowBtn-$gridId\"/>
</div>
";
return $html;
}
/**
* Sets the name of the control
* @param String $name
*/
public function setName($name){
$this->name = $name;
}
/**
* @var String keyfield Key field name holder
*/
private $keyFieldName= "";
/**
* Sets the keyfield name
* @param String $fieldName the key field name
*/
public function setKeyFieldName($fieldName){
$this->keyFieldName = $fieldName;
}
/**
* Gets the key field name
* @return String
*/
public function getKeyFieldName(){
return $this->normalizeFieldName( $this->keyFieldName );
}
/**
* Generates a grid
* whit edit capabilities
*/
public function getInput(){
$doc =& JFactory::getDocument();
$doc->addScript(JURI::base().'administrator/components/com_competences/models/fields/editableassets/jsTable.js');
$doc->addStyleSheet(JURI::base().'administrator/components/com_competences/models/fields/editableassets/jstable-default.css');
if (!count($this->getRows()))
$this->loadData();
$gridId = "tbl".md5(uniqid(time));
$htmlGrid = $this->createTable($gridId);
return
'<div class="editable-wrapper">'
.$htmlGrid
.'<div id="'.$gridId.'" class="editable"></div>'
.'</div>';
}
}
<?php
defined('_JEXEC') or die('Restricted access');
JHTML::_('behavior.mootools');
?>
<script type="text/javascript">
function save(){
document.id('frmtask').value="profile.savelaboralexperience";
document.id('frm-laboral-experience').submit();
}
</script>
<form id = "frm-laboral-experience" method = "post">
<h3><?php echo JText::_('COM_COMPETENCES_PROFILE_PROFESSIONALEXPERIENCE')?></h3>
<div id="list-experience"></div>
<?php
JFormHelper::addFieldPath(JPATH_COMPONENT_ADMINISTRATOR.'/models/fields');
$expLbl = JFormHelper::loadFieldType('EdiTable',false);
$expLbl->setColumns( $this->columns );
$expLbl->setKeyFieldName('id');
$expLbl->setName('laboralExperience');
$cols = array();
$cols[] = array('title'=>'Durada', 'dataType' => 'string', 'maxlength'=>4, 'size' => 4, 'field'=>'contractduration');
$cols[] = array('title'=>'Centre/Organisme', 'dataType' => 'string', 'maxlength'=>30, 'size' => 25,'field'=>'company');
$cols[] = array('title'=>'Sector productiu', 'dataType' => 'string', 'maxlength'=>4, 'size' => 4,'field'=>'idsector');
$cols[] = array('title'=>'Lloc de treball', 'dataType' => 'string', 'maxlength'=>30, 'size' => 30,'field'=>'occupation');
$cols[] = array('title'=>'Tasques', 'dataType' => 'text', 'maxlength'=>30, 'size' => 30,'field'=>'tasks');
$cols[] = array('title'=>'Acciones', 'dataType' => 'actions', 'maxlength'=>0, 'size'=>0);
$expLbl->setRows($cols);
echo $expLbl->getInput();
?>
<div class="toolbar">
<input type="button" value="Guardar" onclick="javascript:save();" />
<input id="frmtask" type="hidden" name="task" value = "" />
<input type="hidden" name="basic[iduser]" value="<?php echo $this->profile->iduser?>" />
<input type="hidden" name="basic[id]" value="<?php echo $this->profile->id?>" />
</div>
</form>
@jlainezs
Copy link
Author

jlainezs commented Feb 4, 2013

This code is under development, so take it as a proof of concept.
In fact, it has security issues as it can suffers an SQL injection. You are warned... use at your own risk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment