Source for file table.php
Documentation is available at table.php
* @package FrameworkOnFramework
* @copyright Copyright (C) 2010 - 2012 Akeeba Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
// Protect from unauthorized access
* Normally this shouldn't be required. Some PHP versions, however, seem to
* require this. Why? No idea whatsoever. If I remove it, FOF crashes on some
* hosts. Same PHP version on another host and no problem occurs. Any takers?
* FrameworkOnFramework Table class. The Table is one part controller, one part
* model and one part data adapter. It's supposed to handle operations for single
* @package FrameworkOnFramework
* Cache array for instances
private static $instances =
array();
* Include paths for searching for FOFTable classes.
private static $_includePaths =
array();
* The configuration parameters array
* Name of the database table to model.
* Name of the primary key field in the table.
* JDatabaseDriver object.
* Should rows be tracked as ACL assets?
* Does the resource support joomla tags?
* The rules associated with this record.
* @var JAccessRules A JAccessRules object.
* Indicator that the tables have been locked.
* If this is set to true, it triggers automatically plugin events for
* Table alias used in queries
* Array with alias for "special" columns such as ordering, hits etc etc
* If set to true, it enabled automatic checks on fields based on columns properties
* Array with fields that should be skipped by automatic checks
* Does the table actually exist? We need that to avoid PHP notices on
* The asset key for items in this table. It's usually something in the
* com_example.viewname format. They asset name will be this key appended
* with the item's ID, e.g. com_example.viewname.123
* Extended query including joins with other tables
* The prefix for the table class
* The known fields for this table
* A list of table fields, keyed per table
protected static $tableFieldCache =
array();
* A list of tables in the database
protected static $tableCache =
array();
* An instance of FOFConfigProvider to provision configuration overrides
* FOFTableDispatcherBehavior for dealing with extra behaviors
* @var FOFTableDispatcherBehavior
* List of default behaviors to apply to the table
* Returns a static object instance of a particular table type
* @param string $type The table name
* @param string $prefix The prefix of the table class
* @param array $config Optional configuration variables
public static function getInstance($type, $prefix =
'JTable', $config =
array())
return self::getAnInstance($type, $prefix, $config);
* Returns a static object instance of a particular table type
* @param string $type The table name
* @param string $prefix The prefix of the table class
* @param array $config Optional configuration variables
public static function &getAnInstance($type =
null, $prefix =
'JTable', $config =
array())
// Make sure $config is an array
$config = (array)
$config;
// Guess the component name
if ($config['input'] instanceof
FOFInput)
$tmpInput =
$config['input'];
$tmpInput =
new FOFInput($config['input']);
$option =
$tmpInput->getCmd('option', '');
$tmpInput->set('option', $option);
$config['input'] =
$tmpInput;
if (!in_array($prefix, array('Table', 'JTable')))
$option =
$config['option'];
$config['option'] =
$option;
$config['view'] =
$config['input']->getCmd('view', 'cpanel');
$tableClass =
$prefix .
ucfirst($type);
if (!class_exists($tableClass))
$componentPaths['main'] .
'/tables',
$componentPaths['admin'] .
'/tables'
$altPath =
$configProvider->get($configProviderKey .
'table_path', null);
array_unshift($searchPaths, $componentPaths['admin'] .
'/' .
$altPath);
$tableClass =
'FOFTable';
$component =
str_replace('com_', '', $config['option']);
$tbl_common =
$component .
'_';
$altTbl =
$configProvider->get($configProviderKey .
'tbl', null);
$config['tbl'] =
$altTbl;
$config['tbl_key'] =
strtolower($tbl_common .
$keyName .
'_id');
$altTblKey =
$configProvider->get($configProviderKey .
'tbl_key', null);
$config['tbl_key'] =
$altTblKey;
// Assign the correct table alias
$table_alias =
$config['table_alias'];
$table_alias =
$configProvider->get($configProviderTableAliasKey, false );
// Can we use the FOF cache?
$alt_use_table_cache =
$configProvider->get($configProviderKey .
'use_table_cache', null);
if (!is_null($alt_use_table_cache))
$config['use_table_cache'] =
$alt_use_table_cache;
// Create a new table instance
$instance =
new $tableClass($config['tbl'], $config['tbl_key'], $config['db'], $config);
$instance->setInput($tmpInput);
$instance->setTablePrefix($prefix);
$instance->setTableAlias($table_alias);
// Determine and set the asset key for this table
$assetKey =
$configProvider->get($configProviderKey .
'asset_key', $assetKey);
$instance->setAssetKey($assetKey);
$instance->setTriggerEvents($config['trigger_events']);
$instance->setHasTags($config['has_tags']);
$altHasTags =
$configProvider->get($configProviderKey .
'has_tags', null);
$instance->setHasTags($altHasTags);
$instance->setHasTags(false);
$aliases =
$configProvider->get($configProviderFieldmapKey, $instance->_columnAlias);
$instance->_columnAlias =
array_merge($instance->_columnAlias, $aliases);
self::$instances[$tableClass] =
$instance;
return self::$instances[$tableClass];
* Force an instance inside class cache. Setting arguments to null nukes all or part of the cache
* @param string|null $key TableClass to replace. Set it to null to nuke the entire cache
* @param FOFTable|null $instance Instance to replace. Set it to null to nuke $key instances
* @return bool Did I correctly switch the instance?
public static function forceInstance($key =
null, $instance =
null)
self::$instances =
array();
elseif($key && isset
(self::$instances[$key]))
// I'm forcing an instance, but it's not a FOFTable, abort! abort!
if(!$instance ||
($instance &&
$instance instanceof
FOFTable))
self::$instances[$key] =
$instance;
* @param string $table Name of the database table to model.
* @param string $key Name of the primary key field in the table.
* @param JDatabaseDriver &$db Database driver
* @param array $config The configuration parameters array
public function __construct($table, $key, &$db, $config =
array())
// Make sure the use FOF cache information is in the config
// Load the configuration provider
// Load the behavior dispatcher
// Initialise the table properties.
// Do I have anything joined?
if ($config['input'] instanceof
FOFInput)
$this->input =
$config['input'];
// Set the $name/$_name variable
$component =
$this->input->getCmd('option', 'com_foobar');
$component =
$config['option'];
$this->input->set('option', $component);
$type =
$type[count($type) -
1];
if (isset
($config['behaviors']))
$behaviors = (array)
$config['behaviors'];
$behaviors =
explode(',', $behaviors);
foreach ($behaviors as $behavior)
// If we are tracking assets, make sure an access field exists and initially set the default.
// If the acess property exists, set the default.
* Replace the entire known fields array
* @param array $fields A simple array of known field names
* @param boolean $initialise Should we initialise variables to null?
* Get the known fields array
* Add a field to the known fields array
* @param string $field The name of the field to add
* @param boolean $initialise Should we initialise the variable to null?
* Remove a field from the known fields array
* @param string $field The name of the field to remove
* Adds a behavior to the table
* @param string $name The name of the behavior
* @param array $config Optional Behavior configuration
* Sets the events trigger switch state
* @param boolean $newState The new state of the switch (what else could it be?)
* Gets the events trigger switch state
* Gets the has tags switch state
* Sets the has tags switch state
// Tags are available only in 3.1+
* @param string $prefix The prefix
* Sets fields to be skipped from automatic checks.
* @param array/string $skip Fields to be skipped by automatic checks
* Method to load a row from the database by primary key and bind the fields
* to the FOFTable instance properties.
* @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. If not
* set the instance property value is used.
* @param boolean $reset True to reset the default values before loading the new row.
* @return boolean True if successful. False if row not found.
* @throws RuntimeException
* @throws UnexpectedValueException
public function load($keys =
null, $reset =
true)
// If empty, use the value of the current key
if (isset
($this->$keyName))
$keyValue =
$this->$keyName;
// If empty primary key there's is no need to load anything
$keys =
array($keyName =>
$keyValue);
$keys =
array($this->_tbl_key =>
$keys);
$query =
$this->_db->getQuery(true);
$query->select($this->_tbl .
'.*');
$query->from($this->_tbl);
// Joined fields are ok, since I initialized them in the constructor
foreach ($keys as $field =>
$value)
// Check that $field is in the table.
throw
new UnexpectedValueException(sprintf('Missing field in table %s : %s.', $this->_tbl, $field));
// Add the search tuple to the query.
$query->where($this->_db->qn($this->_tbl .
'.' .
$field) .
' = ' .
$this->_db->q($value));
// Do I have any joined table?
if ($j_query->select &&
$j_query->select->getElements())
//$query->select($this->normalizeSelectFields($j_query->select->getElements(), true));
$query->select($j_query->select->getElements());
foreach ($j_query->join as $join)
// Joomla doesn't provide any access to the "name" variable, so I have to work with strings...
if (stripos($t, 'inner') !==
false)
$query->innerJoin($join->getElements());
elseif (stripos($t, 'left') !==
false)
$query->leftJoin($join->getElements());
elseif (stripos($t, 'right') !==
false)
$query->rightJoin($join->getElements());
elseif (stripos($t, 'outer') !==
false)
$query->outerJoin($join->getElements());
$this->_db->setQuery($query);
$row =
$this->_db->loadAssoc();
// Check that we have a result.
// Bind the object with the row and return.
$result =
$this->bind($row);
* Based on fields properties (nullable column), checks if the field is required or not
// No fields? Why in the hell am I here?
// Let's merge it with custom skips
foreach ($fields as $field)
$fieldName =
$field->Field;
$fieldName =
$field->column_name;
// Field is not nullable but it's null, set error
if ($field->Null ==
'NO' &&
$this->$fieldName ==
'' &&
!in_array($fieldName, $skipFields))
* Method to reset class properties to the defaults set in the class
* definition. It will ignore the primary key as well as any private class
// Get the default values for the class from the table.
foreach ($fields as $k =>
$v)
// If the property is not the primary key or private, reset it.
* Generic check for whether dependancies exist for this object in the db schema
* @param integer $oid The primary key of the record to delete
* @param array $joins Any joins to foreign table, used to determine if dependent records exist
* @return boolean True if the record can be deleted
public function canDelete($oid =
null, $joins =
null)
$query =
$db->getQuery(true)
->select($db->qn('master') .
'.' .
$db->qn($k))
->from($db->qn($this->_tbl) .
' AS ' .
$db->qn('master'));
foreach ($joins as $table)
'COUNT(DISTINCT ' .
$db->qn('t' .
$tableNo) .
'.' .
$db->qn($table['idfield']) .
') AS ' .
$db->qn($table['idalias'])
$query->join('LEFT', $db->qn($table['name']) .
' AS ' .
$db->qn('t' .
$tableNo) .
' ON ' .
$db->qn('t' .
$tableNo) .
'.' .
$db->qn($table['joinfield']) .
' = ' .
$db->qn('master') .
'.' .
$db->qn($k)
$query->where($db->qn('master') .
'.' .
$db->qn($k) .
' = ' .
$db->q($this->$k));
$query->group($db->qn('master') .
'.' .
$db->qn($k));
$this->_db->setQuery((string)
$query);
$obj =
$this->_db->loadObject();
if (!$obj =
$this->_db->loadObject())
foreach ($joins as $table)
$msg[] =
JText::_($table['label']);
$option =
$this->input->getCmd('option', 'com_foobar');
$prefix =
$option .
'_' .
$tview .
'_NODELETE_';
* Method to bind an associative array or object to the FOFTable instance.This
* method only binds properties that are publicly accessible and optionally
* takes an array of properties to ignore when binding.
* @param mixed $src An associative array or object to bind to the FOFTable instance.
* @param mixed $ignore An optional array or space separated list of properties to ignore while binding.
* @return boolean True on success.
* @throws InvalidArgumentException
public function bind($src, $ignore =
array())
// If the source value is not an array or object return false.
// If the source value is an object, get its accessible properties.
// If the ignore value is a string, explode it over spaces.
// Bind the source value, excluding the ignored fields.
// Only process fields not in the ignore array.
* Method to store a row in the database from the FOFTable instance properties.
* If a primary key value is set the row with that primary key value will be
* updated with the instance property values. If no primary key value is set
* a new row will be inserted into the database with the properties from the
* @param boolean $updateNulls True to update fields even if they are null.
* @return boolean True on success.
public function store($updateNulls =
false)
// Create the object used for inserting/udpating data to the database
foreach ($properties as $property)
// 'input' property is a reserved name
if (isset
($fields[$property]))
$updateObject[$key] =
$this->$key;
$updateObject = (object)
$updateObject;
// If a primary key exists update the object, otherwise insert it.
$result =
$this->_db->updateObject($this->_tbl, $updateObject, $this->_tbl_key, $updateNulls);
$result =
$this->_db->insertObject($this->_tbl, $updateObject, $this->_tbl_key);
$this->bind($updateObject);
* Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause.
* Negative numbers move the row up in the sequence and positive numbers move it down.
* @param integer $delta The direction and magnitude to move the row in the ordering sequence.
* @param string $where WHERE clause to use for limiting the selection of rows to compact the
* @return mixed Boolean True on success.
* @throws UnexpectedValueException
public function move($delta, $where =
'')
// If there is no ordering field set an error and return false.
throw
new UnexpectedValueException(sprintf('%s does not support ordering.', $this->_tbl));
// If the change is none, do nothing.
$query =
$this->_db->getQuery(true);
// If the table is not loaded, return false
// Select the primary key and ordering values from the table.
$query->select(array($this->_db->qn($this->_tbl_key), $this->_db->qn($ordering_field)));
$query->from($this->_tbl);
// If the movement delta is negative move the row up.
$query->where($this->_db->qn($ordering_field) .
' < ' .
$this->_db->q((int)
$this->$ordering_field));
$query->order($this->_db->qn($ordering_field) .
' DESC');
// If the movement delta is positive move the row down.
$query->where($this->_db->qn($ordering_field) .
' > ' .
$this->_db->q((int)
$this->$ordering_field));
$query->order($this->_db->qn($ordering_field) .
' ASC');
// Add the custom WHERE clause if set.
// Select the first row with the criteria.
$this->_db->setQuery($query, 0, 1);
$row =
$this->_db->loadObject();
// If a row is found, move the item.
// Update the ordering field for this instance to the row's ordering value.
$query =
$this->_db->getQuery(true);
$query->update($this->_tbl);
$query->set($this->_db->qn($ordering_field) .
' = ' .
$this->_db->q((int)
$row->$ordering_field));
$query->where($this->_tbl_key .
' = ' .
$this->_db->q($this->$k));
$this->_db->setQuery($query);
// Update the ordering field for the row to this instance's ordering value.
$query =
$this->_db->getQuery(true);
$query->update($this->_tbl);
$query->set($this->_db->qn($ordering_field) .
' = ' .
$this->_db->q((int)
$this->$ordering_field));
$query->where($this->_tbl_key .
' = ' .
$this->_db->q($row->$k));
$this->_db->setQuery($query);
// Update the instance value.
$this->$ordering_field =
$row->$ordering_field;
// Update the ordering field for this instance.
$query =
$this->_db->getQuery(true);
$query->update($this->_tbl);
$query->set($this->_db->qn($ordering_field) .
' = ' .
$this->_db->q((int)
$this->$ordering_field));
$query->where($this->_tbl_key .
' = ' .
$this->_db->q($this->$k));
$this->_db->setQuery($query);
* Change the ordering of the records of the table
* @param string $where The WHERE clause of the SQL used to fetch the order
* @return boolean True is successful
* @throws UnexpectedValueException
public function reorder($where =
'')
// If there is no ordering field set an error and return false.
throw
new UnexpectedValueException(sprintf('%s does not support ordering.', $this->_tbl_key));
// Get the primary keys and ordering values for the selection.
$query =
$this->_db->getQuery(true);
$query->select($this->_tbl_key .
', ' .
$this->_db->qn($order_field));
$query->from($this->_tbl);
$query->where($this->_db->qn($order_field) .
' >= ' .
$this->_db->q(0));
$query->order($this->_db->qn($order_field));
// Setup the extra where and ordering clause data.
$this->_db->setQuery($query);
$rows =
$this->_db->loadObjectList();
// Compact the ordering values.
foreach ($rows as $i =>
$row)
// Make sure the ordering is a positive integer.
if ($row->$order_field >=
0)
// Only update rows that are necessary.
if ($row->$order_field !=
$i +
1)
// Update the row ordering field.
$query =
$this->_db->getQuery(true);
$query->update($this->_tbl);
$query->set($this->_db->qn($order_field) .
' = ' .
$this->_db->q($i +
1));
$query->where($this->_tbl_key .
' = ' .
$this->_db->q($row->$k));
$this->_db->setQuery($query);
* Check out (lock) a record
* @param integer $userId The locking user's ID
* @param integer $oid The primary key value of the record to lock
* @return boolean True on success
public function checkout($userId, $oid =
null)
// No primary key defined, stop here
$query =
$this->_db->getQuery(true)
->update($this->_db->qn($this->_tbl))
$this->_db->qn($fldLockedBy) .
' = ' .
$this->_db->q((int)
$userId),
$this->_db->qn($fldLockedOn) .
' = ' .
$this->_db->q($time)
->where($this->_db->qn($this->_tbl_key) .
' = ' .
$this->_db->q($this->$k));
$this->_db->setQuery((string)
$query);
$this->$fldLockedBy =
$userId;
$this->$fldLockedOn =
$time;
return $this->_db->execute();
* Check in (unlock) a record
* @param integer $oid The primary key value of the record to unlock
* @return boolean True on success
public function checkin($oid =
null)
$query =
$this->_db->getQuery(true)
->update($this->_db->qn($this->_tbl))
$this->_db->qn($fldLockedBy) .
' = 0',
$this->_db->qn($fldLockedOn) .
' = ' .
$this->_db->q($this->_db->getNullDate())
->where($this->_db->qn($this->_tbl_key) .
' = ' .
$this->_db->q($this->$k));
$this->_db->setQuery((string)
$query);
$this->$fldLockedOn =
'';
return $this->_db->execute();
* @param integer $with The userid to preform the match with. If an item is checked
* out by this user the function will return false.
* @param integer $unused_against Junk inherited from JTable; ignore
* @throws UnexpectedValueException
* @return boolean True if the record is locked by another user
public function isCheckedOut($with =
0, $unused_against =
null)
// If no primary key is given, return false.
throw
new UnexpectedValueException('Null primary key not allowed.');
if (isset
($this) &&
is_a($this, 'FOFTable') &&
!$against)
$against =
$this->get($fldLockedBy);
// Item is not checked out, or being checked out by the same user
if (!$against ||
$against ==
$with)
return $session->exists($against);
* Copy (duplicate) one or more records
* @param integer|array $cid The primary key value (or values) or the record(s) to copy
* @return boolean True on success
public function copy($cid =
null)
//We have to cast the id as array, or the helper function will return an empty set
// Prevent load with id = 0
// We're using the checkin and the record is used by someone else
// TODO Should we notify the user that we had a problem with this record?
$this->$created_by =
null;
$this->$created_on =
null;
$this->$modified_on =
null;
$this->$modified_by =
null;
// Let's fire the event only if everything is ok
// TODO Should we notify the user that we had a problem with this record?
// TODO Should we notify the user that we had a problem with this record?
* Publish or unpublish records
* @param integer|array $cid The primary key value(s) of the item(s) to publish/unpublish
* @param integer $publish 1 to publish an item, 0 to unpublish
* @param integer $user_id The user ID of the user (un)publishing the item.
* @return boolean True on success, false on failure (e.g. record is locked)
public function publish($cid =
null, $publish =
1, $user_id =
0)
// Mhm... you called the publish method on a table without publish support...
//We have to cast the id as array, or the helper function will return an empty set
$user_id = (int)
$user_id;
$publish = (int)
$publish;
$query =
$this->_db->getQuery(true)
->update($this->_db->qn($this->_tbl))
->set($this->_db->qn($enabledName) .
' = ' . (int)
$publish);
' (' .
$this->_db->qn($locked_byName) .
' = 0 OR ' .
$this->_db->qn($locked_byName) .
' = ' . (int)
$user_id .
')', 'AND'
//Why this crazy statement?
// TODO Rewrite this statment using IN. Check if it work in SQLServer and PostgreSQL
$cids =
$this->_db->qn($k) .
' = ' .
implode(' OR ' .
$this->_db->qn($k) .
' = ', $cid);
$query->where('(' .
$cids .
')');
$this->_db->setQuery((string)
$query);
if (!$this->_db->execute())
if (count($cid) ==
1 &&
$checkin)
if ($this->_db->getAffectedRows() ==
1)
// TODO should we check for its return value?
if ($this->$k ==
$cid[0])
$this->$enabledName =
$publish;
* @param integer $oid The primary key value of the item to delete
* @throws UnexpectedValueException
* @return boolean True on success
public function delete($oid =
null)
$pk =
(!$oid) ?
$this->$k :
$oid;
// If no primary key is given, return false.
throw
new UnexpectedValueException('Null primary key not allowed.');
// Execute the logic only if I have a primary key, otherwise I could have weird results
// Delete the row by primary key.
$query =
$this->_db->getQuery(true);
$query->from($this->_tbl);
$query->where($this->_tbl_key .
' = ' .
$this->_db->q($pk));
$this->_db->setQuery($query);
// @TODO Check for a database error.
* Register a hit on a record
* @param integer $oid The primary key value of the record
* @param boolean $log Should I log the hit?
* @return boolean True on success
public function hit($oid =
null, $log =
false)
// If there is no hits field, just return true.
$pk =
(is_null($oid)) ?
$this->$k :
$oid;
// If no primary key is given, return false.
// Check the row in by primary key.
$query =
$this->_db->getQuery(true);
$query->update($this->_tbl);
$query->set($this->_db->qn($hits_field) .
' = (' .
$this->_db->qn($hits_field) .
' + 1)');
$query->where($this->_tbl_key .
' = ' .
$this->_db->q($pk));
$this->_db->setQuery($query);
// Set table values in the object.
* Export the item as a CSV line
* @param string $separator CSV separator. Tip: use "\t" to get a TSV file instead.
* @return string The CSV line
public function toCSV($separator =
',')
* Exports the table in array format
// Special internal fields
if (in_array($k, array('config', 'input', 'knownFields')))
if (($k[0] ==
'_') ||
($k[0] ==
'*'))
* Get the header for exporting item list to CSV
* @param string $separator CSV separator. Tip: use "\t" to get a TSV file instead.
* @return string The CSV file's header
* Get the columns from a database table.
* @param string $tableName Table name. If null current table is used
* @return mixed An array of the field names, or false if an error occurs.
// Should I load the cached data?
// Make sure we have a list of tables in this db
if (empty(self::$tableCache))
// Try to load table cache from a cache file
$cacheData =
FOFPlatform::getInstance()->getCache('tables', null);
// Unserialise the cached data, or set the table cache to empty
// if the cache data wasn't loaded.
self::$tableCache =
json_decode($cacheData, true);
self::$tableCache =
array();
// This check is true if the cache data doesn't exist / is not loaded
if (empty(self::$tableCache))
self::$tableCache =
$this->_db->getTableList();
FOFPlatform::getInstance()->setCache('tables', json_encode(self::$tableCache));
// Make sure the cached table fields cache is loaded
if (empty(self::$tableFieldCache))
// Try to load table cache from a cache file
$cacheData =
FOFPlatform::getInstance()->getCache('tablefields', null);
// Unserialise the cached data, or set to empty if the cache
foreach ($decoded as $myTableName =>
$tableFields)
foreach($tableFields as $field =>
$def)
$temp[$field] = (object)
$def;
$tableCache[$myTableName] =
$temp;
$tableCache[$myTableName] =
$tableFields;
self::$tableFieldCache =
$tableCache;
self::$tableFieldCache =
array();
$tableName =
$this->_tbl;
// Try to load again column specifications if the table is not loaded OR if it's loaded and
// the previous call returned an error
if (!array_key_exists($tableName, self::$tableFieldCache) ||
(isset
(self::$tableFieldCache[$tableName]) &&
!self::$tableFieldCache[$tableName]))
// Lookup the fields for this table only once.
$prefix =
$this->_db->getPrefix();
if (substr($name, 0, 3) ==
'#__')
$checkName =
$prefix .
substr($name, 3);
if (!in_array($checkName, self::$tableCache))
// The table doesn't exist. Return false.
self::$tableFieldCache[$tableName] =
false;
$fields =
$this->_db->getTableColumns($name, false);
self::$tableFieldCache[$tableName] =
$fields;
$fields =
$this->_db->getTableFields($name, false);
if (!isset
($fields[$name]))
self::$tableFieldCache[$tableName] =
$fields[$name];
// PostgreSQL date type compatibility
if (($this->_db->name ==
'postgresql') &&
(self::$tableFieldCache[$tableName] !=
false))
foreach (self::$tableFieldCache[$tableName] as $field)
if (strtolower($field->type) ==
'timestamp without time zone')
if (stristr($field->Default, '\'::timestamp without time zone'))
list
($date, $junk) =
explode('::', $field->Default, 2);
$field->Default =
trim($date, "'");
// Save the data for this table into the cache
return self::$tableFieldCache[$tableName];
$string =
preg_replace('#[^A-Z0-9_]#i', '', $string);
* Method to return the real name of a "special" column such as ordering, hits, published
* etc etc. In this way you are free to follow your db naming convention and use the
* built in Joomla functions.
* @param string $column Name of the "special" column (ie ordering, hits etc etc)
* @return string The string that identify the special
* Method to register a column alias for a "special" column.
* @param string $column The "special" column (ie ordering)
* @param string $columnAlias The real column name (ie foo_ordering)
* Get a JOIN query, used to join other tables
* @param boolean $asReference Return an object reference instead of a copy
* @return JDatabaseQuery Query used to join other tables
* Sets the query with joins to other tables
* @param JDatabaseQuery $query The JOIN query to use
* Extracts the fields from the join query
* @return array Fields contained in the join query
// Get joined tables. Ignore FROM clause, since it should not be used (the starting point is the table "table")
foreach ($joins as $join)
foreach($tables as $table)
preg_match('#(.*)((\w)*(on|using))(.*)#i', $table, $matches);
if($matches && isset
($matches[1]))
// I always want the first part, no matter what
$parts =
explode(' ', $matches[1]);
// Do I have the current table inside the query join? Remove it (its fields are already ok)
foreach ($j_tables as $table)
// Remove any fields that aren't in the joined select
$j_select =
$query->select;
if ($j_select &&
$j_select->getElements())
// I can intesect the keys
// Now I walk again the array to change the key of columns that have an alias
foreach ($j_fields as $column =>
$alias)
$fields[$alias] =
$fields[$column];
* Normalizes the fields, returning an associative array with all the fields.
* Ie array('foobar as foo, bar') becomes array('foobar' => 'foo', 'bar' => 'bar')
* @param array $fields Array with column fields
* @return array Normalized array
foreach ($fields as $field)
foreach ($t_fields as $t_field)
// Do I have a table.column situation? Let's get the field name
$tableField =
explode('.', $parts[0]);
if(isset
($tableField[1]))
$column =
trim($tableField[1]);
$column =
trim($tableField[0]);
// Is this field quoted? If so, remove the quotes
$alias =
trim($parts[1]);
// Is this field quoted? If so, remove the quotes
$return[$column] =
$alias;
* @param string $column Column, passed by reference, so in later version of Joomla
* I can always quote them
* @return bool Is the field quoted?
// Under Joomla 3.2 I can safely quote the column again, then return true
// On previous version I need some "magic". If the first char is not a letter, a number
// an underscore or # (needed for table), then most likely the field is quoted
* The event which runs before binding data to the table
* NOTE TO 3RD PARTY DEVELOPERS:
* When you override the following methods in your child classes,
* be sure to call parent::method *AFTER* your code, otherwise the
* plugin events do NOT get triggered
* protected function onAfterStore(){
* return parent::onAfterStore() && $your_result;
* Do not do it the other way around, e.g. return $your_result && parent::onAfterStore()
* Due to PHP short-circuit boolean evaluation the parent::onAfterStore()
* will not be called if $your_result is false.
* @param object|array &$from The data to bind
* @return boolean True on success
$result =
$this->tableDispatcher->trigger('onBeforeBind', array(&$this, &$from));
// Behavior failed, return false
* The event which runs after loading a record from the database
* @param boolean &$result Did the load succeeded?
$eventRistult =
$this->tableDispatcher->trigger('onAfterLoad', array(&$this, &$result));
if (in_array(false, $eventRistult, true))
// Behavior failed, return false
* The event which runs before storing (saving) data to the database
* @param boolean $updateNulls Should nulls be saved as nulls (true) or just skipped over (false)?
* @return boolean True to allow saving
// Do we have a "Created" set of fields?
if ($hasCreatedOn &&
$hasCreatedBy)
$nullDate =
$this->_db->getNullDate();
if (empty($this->$created_by) ||
($this->$created_on ==
$nullDate) ||
empty($this->$created_on))
$this->$created_on =
$date->toSql();
elseif ($hasModifiedOn &&
$hasModifiedBy)
$this->$modified_on =
$date->toSql();
// Do we have a set of title and slug fields?
if ($hasTitle &&
$hasSlug)
// Create a slug from the title
// Filter the slug for invalid characters
// Make sure we don't have a duplicate slug on this table
$query =
$db->getQuery(true)
->where($db->qn($slug) .
' = ' .
$db->q($this->$slug))
->where('NOT ' .
$db->qn($this->_tbl_key) .
' = ' .
$db->q($this->{$this->_tbl_key}));
$existingItems =
$db->loadAssocList();
while (!empty($existingItems))
$newSlug =
$this->$slug .
'-' .
$count;
$query =
$db->getQuery(true)
->where($db->qn($slug) .
' = ' .
$db->q($newSlug))
->where($db->qn($this->_tbl_key) .
' = ' .
$db->q($this->{$this->_tbl_key}), 'AND NOT');
$existingItems =
$db->loadAssocList();
$result =
$this->tableDispatcher->trigger('onBeforeStore', array(&$this, $updateNulls));
// Behavior failed, return false
// Execute onBeforeStore<tablename> events in loaded plugins
* The event which runs after binding data to the class
* @param object|array &$src The data to bind
* @return boolean True to allow binding without an error
'component' =>
$this->input->get('option'),
$result =
$this->tableDispatcher->trigger('onAfterBind', array(&$this, &$src, $options));
// Behavior failed, return false
* The event which runs after storing (saving) data to the database
* @return boolean True to allow saving without an error
// Behavior failed, return false
* The event which runs before moving a record
* @param boolean $updateNulls Should nulls be saved as nulls (true) or just skipped over (false)?
* @return boolean True to allow moving
$result =
$this->tableDispatcher->trigger('onBeforeMove', array(&$this, $updateNulls));
// Behavior failed, return false
* The event which runs after moving a record
* @return boolean True to allow moving without an error
// Behavior failed, return false
* The event which runs before reordering a table
* @param string $where The WHERE clause of the SQL query to run on reordering (record filter)
* @return boolean True to allow reordering
$result =
$this->tableDispatcher->trigger('onBeforeReorder', array(&$this, $where));
// Behavior failed, return false
* The event which runs after reordering a table
* @return boolean True to allow the reordering to complete without an error
$result =
$this->tableDispatcher->trigger('onAfterReorder', array(&$this));
// Behavior failed, return false
* The event which runs before deleting a record
* @param integer $oid The PK value of the record to delete
* @return boolean True to allow the deletion
$result =
$this->tableDispatcher->trigger('onBeforeDelete', array(&$this, $oid));
// Behavior failed, return false
* The event which runs after deleting a record
* @param integer $oid The PK value of the record which was deleted
* @return boolean True to allow the deletion without errors
$result =
$this->tableDispatcher->trigger('onAfterDelete', array(&$this, $oid));
// Behavior failed, return false
* The event which runs before hitting a record
* @param integer $oid The PK value of the record to hit
* @param boolean $log Should we log the hit?
* @return boolean True to allow the hit
$result =
$this->tableDispatcher->trigger('onBeforeHit', array(&$this, $oid, $log));
// Behavior failed, return false
* The event which runs after hitting a record
* @param integer $oid The PK value of the record which was hit
* @return boolean True to allow the hitting without errors
$result =
$this->tableDispatcher->trigger('onAfterHit', array(&$this, $oid));
// Behavior failed, return false
* The even which runs before copying a record
* @param integer $oid The PK value of the record being copied
* @return boolean True to allow the copy to take place
$result =
$this->tableDispatcher->trigger('onBeforeCopy', array(&$this, $oid));
// Behavior failed, return false
* The even which runs after copying a record
* @param integer $oid The PK value of the record which was copied (not the new one)
* @return boolean True to allow the copy without errors
$result =
$this->tableDispatcher->trigger('onAfterCopy', array(&$this, $oid));
// Behavior failed, return false
* The event which runs before a record is (un)published
* @param integer|array &$cid The PK IDs of the records being (un)published
* @param integer $publish 1 to publish, 0 to unpublish
* @return boolean True to allow the (un)publish to proceed
$result =
$this->tableDispatcher->trigger('onBeforePublish', array(&$this, &$cid, $publish));
// Behavior failed, return false
* The event which runs after the object is reset to its default values.
* @return boolean True to allow the reset to complete without errors
// Behavior failed, return false
* The even which runs before the object is reset to its default values.
* @return boolean True to allow the reset to complete
// Behavior failed, return false
* Replace the input object of this table with the provided FOFInput object
* @param FOFInput $input The new input object
public function setInput(FOFInput $input)
* Get the columns from database table.
* @return mixed An array of the field names, or false if an error occurs.
* Add a filesystem path where FOFTable should search for table class files.
* You may either pass a string or an array of paths.
* @param mixed $path A filesystem path or array of filesystem paths to add.
* @return array An array of filesystem paths to find FOFTable classes in.
// If the internal paths have not been initialised, do so with the base table path.
if (empty(self::$_includePaths))
self::$_includePaths =
array(__DIR__
);
// Convert the passed path(s) to add to an array.
// If we have new paths to add, do so.
if (!empty($path) &&
!in_array($path, self::$_includePaths))
// Check and add each individual new path.
// Add to the front of the list so that custom paths are searched first.
return self::$_includePaths;
* Loads the asset table related to this table.
* This will help tests, too, since we can mock this function.
* @return bool|JTableAsset False on failure, otherwise JTableAsset
$name =
$this->_getAssetName();
// Do NOT touch JTable here -- we are loading the core asset table which is a JTable, not a FOFTable
$asset =
JTable::getInstance('Asset');
if (!$asset->loadByName($name))
* Method to compute the default name of the asset.
* The default name is in the form table_name.id
* where id is the value of the primary key of the table.
* @throws UnexpectedValueException
// If there is no assetKey defined, let's set it to table name
throw
new UnexpectedValueException('Table must have an asset key defined in order to track assets');
return $this->_assetKey .
'.' . (int)
$this->$k;
* Method to compute the default name of the asset.
* The default name is in the form table_name.id
* where id is the value of the primary key of the table.
* @throws UnexpectedValueException
* Method to return the title to use for the asset table. In
* tracking the assets a title is kept for each asset so that there is some
* context available in a unified access manager. Usually this would just
* return $this->title or $this->name or whatever is being used for the
* primary name of the row. If this method is not overridden, the asset name is used.
* @return string The string to use as the title in the asset table.
* Method to get the parent asset under which to register this one.
* By default, all assets are registered to the ROOT node with ID,
* which will default to 1 if none exists.
* The extended class can define a table and id to lookup. If the
* asset does not exist it will be created.
* @param FOFTable $table A FOFTable object for the asset parent.
* @param integer $id Id to look up
// For simple cases, parent to the asset root.
$rootId =
$assets->getRootId();
* This method sets the asset key for the items of this table. Obviously, it
* is only meant to be used when you have a table with an asset field.
* @param string $assetKey The name of the asset key to use
* Method to get the database table name for the class.
* @return string The name of the database table being modeled.
* Method to get the primary key field name for the table.
* @return string The name of the primary key for the table.
* Method to get the JDatabaseDriver object.
* @return JDatabaseDriver The internal database driver object.
* Method to set the JDatabaseDriver object.
* @param JDatabaseDriver $db A JDatabaseDriver object to be used by the table object.
* @return boolean True on success.
public function setDBO(JDatabaseDriver $db)
* Method to set rules for the record.
* @param mixed $input A JAccessRules object, JSON string, or array.
* Method to get the rules for the record.
* @return JAccessRules object
* Method to check if the record is treated as an ACL asset
* @return boolean [description]
* Method to provide a shortcut to binding, checking and storing a FOFTable
* instance to the database table. The method will check a row in once the
* data has been stored and if an ordering filter is present will attempt to
* reorder the table rows based on the filter. The ordering filter is an instance
* property name. The rows that will be reordered are those whose value matches
* the FOFTable instance for the property specified.
* @param mixed $src An associative array or object to bind to the FOFTable instance.
* @param string $orderingFilter Filter for the order updating
* @param mixed $ignore An optional array or space separated list of properties
* to ignore while binding.
* @return boolean True on success.
public function save($src, $orderingFilter =
'', $ignore =
'')
// Attempt to bind the source to the instance.
if (!$this->bind($src, $ignore))
// Run any sanity checks on the instance and verify that it is ready for storage.
// Attempt to store the properties to the database table.
// Attempt to check the row in, just in case it was checked out.
// If an ordering filter is set, attempt reorder the rows in the table based on the filter and value.
$filterValue =
$this->$orderingFilter;
$this->reorder($orderingFilter ?
$this->_db->qn($orderingFilter) .
' = ' .
$this->_db->q($filterValue) :
'');
// Set the error to empty and return true.
* Method to get the next ordering value for a group of rows defined by an SQL WHERE clause.
* This is useful for placing a new item last in a group of items in the table.
* @param string $where WHERE clause to use for selecting the MAX(ordering) for the table.
* @return mixed Boolean false an failure or the next ordering value as an integer.
// If there is no ordering field set an error and return false.
throw
new UnexpectedValueException(sprintf('%s does not support ordering.', get_class($this)));
// Get the largest ordering value for a given where clause.
$query =
$this->_db->getQuery(true);
$query->select('MAX('.
$this->_db->qn($ordering).
')');
$query->from($this->_tbl);
$this->_db->setQuery($query);
$max = (int)
$this->_db->loadResult();
// Return the largest ordering value + 1.
* Method to lock the database table for writing.
* @return boolean True on success.
* @throws RuntimeException
protected function _lock()
$this->_db->lockTable($this->_tbl);
* Method to unlock the database table for writing.
* @return boolean True on success.
$this->_db->unlockTables();
* Get the content type for ucm
* @return string The content type alias
public function getContentType()
$component =
$this->input->get('option');
$alias =
$component .
'.' .
$view;
Documentation generated on Tue, 19 Nov 2013 15:15:02 +0100 by phpDocumentor 1.4.3