* David C. Morse * Mark Wiesemann * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @category Database * @package DB_Table * @author Paul M. Jones * @author David C. Morse * @author Mark Wiesemann * @license http://opensource.org/licenses/bsd-license.php New BSD License * @version CVS: $Id: Table.php,v 1.90 2008/12/25 19:56:35 wiesemann Exp $ * @link http://pear.php.net/package/DB_Table */ /** * Error code at instantiation time when the first parameter to the * constructor is not a PEAR DB object. */ define('DB_TABLE_ERR_NOT_DB_OBJECT', -1); /** * Error code at instantiation time when the PEAR DB/MDB2 $phptype is not * supported by DB_Table. */ define('DB_TABLE_ERR_PHPTYPE', -2); /** * Error code when you call select() or selectResult() and the first * parameter is a string that does not match any of the $this->sql keys. */ define('DB_TABLE_ERR_SQL_UNDEF', -3); /** * Error code when you call select*() or buildSQL() and the first * parameter is neither an array nor a string */ define('DB_TABLE_ERR_SQL_NOT_STRING', -4); /** * Error code when you try to insert data to a column that is not in the * $this->col array. */ define('DB_TABLE_ERR_INS_COL_NOMAP', -5); /** * Error code when you try to insert data, and that data does not have a * column marked as 'require' in the $this->col array. */ define('DB_TABLE_ERR_INS_COL_REQUIRED', -6); /** * Error code when auto-validation fails on data to be inserted. */ define('DB_TABLE_ERR_INS_DATA_INVALID', -7); /** * Error code when you try to update data to a column that is not in the * $this->col array. */ define('DB_TABLE_ERR_UPD_COL_NOMAP', -8); /** * Error code when you try to update data, and that data does not have a * column marked as 'require' in the $this->col array. */ define('DB_TABLE_ERR_UPD_COL_REQUIRED', -9); /** * Error code when auto-validation fails on update data. */ define('DB_TABLE_ERR_UPD_DATA_INVALID', -10); /** * Error code when you use a create() flag that is not recognized (must * be 'safe', 'drop', 'verify' or boolean false. */ define('DB_TABLE_ERR_CREATE_FLAG', -11); /** * Error code at create() time when you define an index in $this->idx * that has no columns. */ define('DB_TABLE_ERR_IDX_NO_COLS', -12); /** * Error code at create() time when you define an index in $this->idx * that refers to a column that does not exist in the $this->col array. */ define('DB_TABLE_ERR_IDX_COL_UNDEF', -13); /** * Error code at create() time when you define a $this->idx index type * that is not recognized (must be 'normal' or 'unique'). */ define('DB_TABLE_ERR_IDX_TYPE', -14); /** * Error code at create() time when you have an error in a 'char' or * 'varchar' definition in $this->col (usually because 'size' is wrong). */ define('DB_TABLE_ERR_DECLARE_STRING', -15); /** * Error code at create() time when you have an error in a 'decimal' * definition (usually becuase the 'size' or 'scope' are wrong). */ define('DB_TABLE_ERR_DECLARE_DECIMAL', -16); /** * Error code at create() time when you define a column in $this->col * with an unrecognized 'type'. */ define('DB_TABLE_ERR_DECLARE_TYPE', -17); /** * Error code at validation time when a column in $this->col has an * unrecognized 'type'. */ define('DB_TABLE_ERR_VALIDATE_TYPE', -18); /** * Error code at create() time when you define a column in $this->col * with an invalid column name (usually because it's a reserved keyword). */ define('DB_TABLE_ERR_DECLARE_COLNAME', -19); /** * Error code at create() time when you define an index in $this->idx * with an invalid index name (usually because it's a reserved keyword). */ define('DB_TABLE_ERR_DECLARE_IDXNAME', -20); /** * Error code at create() time when you define an index in $this->idx * that refers to a CLOB column. */ define('DB_TABLE_ERR_IDX_COL_CLOB', -21); /** * Error code at create() time when you define a column name that is * more than 30 chars long (an Oracle restriction). */ define('DB_TABLE_ERR_DECLARE_STRLEN', -22); /** * Error code at create() time when the index name ends up being more * than 30 chars long (an Oracle restriction). */ define('DB_TABLE_ERR_IDX_STRLEN', -23); /** * Error code at create() time when the table name is more than 30 chars * long (an Oracle restriction). */ define('DB_TABLE_ERR_TABLE_STRLEN', -24); /** * Error code at nextID() time when the sequence name is more than 30 * chars long (an Oracle restriction). */ define('DB_TABLE_ERR_SEQ_STRLEN', -25); /** * Error code at verify() time when the table does not exist in the * database. */ define('DB_TABLE_ERR_VER_TABLE_MISSING', -26); /** * Error code at verify() time when the column does not exist in the * database table. */ define('DB_TABLE_ERR_VER_COLUMN_MISSING', -27); /** * Error code at verify() time when the column type does not match the * type specified in the column declaration. */ define('DB_TABLE_ERR_VER_COLUMN_TYPE', -28); /** * Error code at instantiation time when the column definition array * does not contain at least one column. */ define('DB_TABLE_ERR_NO_COLS', -29); /** * Error code at verify() time when an index cannot be found in the * database table. */ define('DB_TABLE_ERR_VER_IDX_MISSING', -30); /** * Error code at verify() time when an index does not contain all * columns that it should contain. */ define('DB_TABLE_ERR_VER_IDX_COL_MISSING', -31); /** * Error code at instantiation time when a creation mode * is not available for a phptype. */ define('DB_TABLE_ERR_CREATE_PHPTYPE', -32); /** * Error code at create() time when you define more than one primary key * in $this->idx. */ define('DB_TABLE_ERR_DECLARE_PRIMARY', -33); /** * Error code at create() time when a primary key is defined in $this->idx * and SQLite is used (SQLite does not support primary keys). */ define('DB_TABLE_ERR_DECLARE_PRIM_SQLITE', -34); /** * Error code at alter() time when altering a table field is not possible * (e.g. because MDB2 has no support for the change or because the DBMS * does not support the change). */ define('DB_TABLE_ERR_ALTER_TABLE_IMPOS', -35); /** * Error code at alter() time when altering a(n) index/constraint is not possible * (e.g. because MDB2 has no support for the change or because the DBMS * does not support the change). */ define('DB_TABLE_ERR_ALTER_INDEX_IMPOS', -36); /** * Error code at insert() time due to invalid the auto-increment column * definition. This column must be an integer type and required. */ define('DB_TABLE_ERR_AUTO_INC_COL', -37); /** * Error code at instantiation time when both the $table parameter * and the $table class property are missing. */ define('DB_TABLE_ERR_TABLE_NAME_MISSING', -38); /** * The DB_Table_Base parent class */ require_once 'DB/Table/Base.php'; /** * The PEAR class for errors */ require_once 'PEAR.php'; /** * The Date class for recasting date and time values */ require_once 'DB/Table/Date.php'; /** * DB_Table supports these RDBMS engines and their various native data * types; we need these here instead of in Manager.php because the * initial array key tells us what databases are supported. */ $GLOBALS['_DB_TABLE']['type'] = array( 'fbsql' => array( 'boolean' => 'DECIMAL(1,0)', 'char' => 'CHAR', 'varchar' => 'VARCHAR', 'smallint' => 'SMALLINT', 'integer' => 'INTEGER', 'bigint' => 'LONGINT', 'decimal' => 'DECIMAL', 'single' => 'REAL', 'double' => 'DOUBLE PRECISION', 'clob' => 'CLOB', 'date' => 'CHAR(10)', 'time' => 'CHAR(8)', 'timestamp' => 'CHAR(19)' ), 'ibase' => array( 'boolean' => 'DECIMAL(1,0)', 'char' => 'CHAR', 'varchar' => 'VARCHAR', 'smallint' => 'SMALLINT', 'integer' => 'INTEGER', 'bigint' => 'BIGINT', 'decimal' => 'DECIMAL', 'single' => 'FLOAT', 'double' => 'DOUBLE PRECISION', 'clob' => 'BLOB SUB_TYPE 1', 'date' => 'DATE', 'time' => 'TIME', 'timestamp' => 'TIMESTAMP' ), 'mssql' => array( 'boolean' => 'DECIMAL(1,0)', 'char' => 'CHAR', 'varchar' => 'VARCHAR', 'smallint' => 'SMALLINT', 'integer' => 'INTEGER', 'bigint' => 'BIGINT', 'decimal' => 'DECIMAL', 'single' => 'REAL', 'double' => 'FLOAT', 'clob' => 'TEXT', 'date' => 'CHAR(10)', 'time' => 'CHAR(8)', 'timestamp' => 'CHAR(19)' ), 'mysql' => array( 'boolean' => 'DECIMAL(1,0)', 'char' => 'CHAR', 'varchar' => 'VARCHAR', 'smallint' => 'SMALLINT', 'integer' => 'INTEGER', 'bigint' => 'BIGINT', 'decimal' => 'DECIMAL', 'single' => 'FLOAT', 'double' => 'DOUBLE', 'clob' => 'LONGTEXT', 'date' => 'CHAR(10)', 'time' => 'CHAR(8)', 'timestamp' => 'CHAR(19)' ), 'mysqli' => array( 'boolean' => 'DECIMAL(1,0)', 'char' => 'CHAR', 'varchar' => 'VARCHAR', 'smallint' => 'SMALLINT', 'integer' => 'INTEGER', 'bigint' => 'BIGINT', 'decimal' => 'DECIMAL', 'single' => 'FLOAT', 'double' => 'DOUBLE', 'clob' => 'LONGTEXT', 'date' => 'CHAR(10)', 'time' => 'CHAR(8)', 'timestamp' => 'CHAR(19)' ), 'oci8' => array( 'boolean' => 'NUMBER(1)', 'char' => 'CHAR', 'varchar' => 'VARCHAR2', 'smallint' => 'NUMBER(6)', 'integer' => 'NUMBER(11)', 'bigint' => 'NUMBER(19)', 'decimal' => 'NUMBER', 'single' => 'REAL', 'double' => 'DOUBLE PRECISION', 'clob' => 'CLOB', 'date' => 'CHAR(10)', 'time' => 'CHAR(8)', 'timestamp' => 'CHAR(19)' ), 'pgsql' => array( 'boolean' => 'DECIMAL(1,0)', 'char' => 'CHAR', 'varchar' => 'VARCHAR', 'smallint' => 'SMALLINT', 'integer' => 'INTEGER', 'bigint' => 'BIGINT', 'decimal' => 'DECIMAL', 'single' => 'REAL', 'double' => 'DOUBLE PRECISION', 'clob' => 'TEXT', 'date' => 'CHAR(10)', 'time' => 'CHAR(8)', 'timestamp' => 'CHAR(19)' ), 'sqlite' => array( 'boolean' => 'BOOLEAN', 'char' => 'CHAR', 'varchar' => 'VARCHAR', 'smallint' => 'SMALLINT', 'integer' => 'INTEGER', 'bigint' => 'BIGINT', 'decimal' => 'NUMERIC', 'single' => 'FLOAT', 'double' => 'DOUBLE', 'clob' => 'CLOB', 'date' => 'DATE', 'time' => 'TIME', 'timestamp' => 'TIMESTAMP' ) ); /** * US-English default error messages. If you want to internationalize, you can * set the translated messages via $GLOBALS['_DB_TABLE']['error']. You can also * use DB_Table::setErrorMessage(). Examples: * * * (1) $GLOBALS['_DB_TABLE]['error'] = array(DB_TABLE_ERR_PHPTYPE => '...', * DB_TABLE_ERR_SQL_UNDEF => '...'); * (2) DB_Table::setErrorMessage(DB_TABLE_ERR_PHPTYPE, '...'); * DB_Table::setErrorMessage(DB_TABLE_ERR_SQL_UNDEF, '...'); * (3) DB_Table::setErrorMessage(array(DB_TABLE_ERR_PHPTYPE => '...'); * DB_TABLE_ERR_SQL_UNDEF => '...'); * (4) $obj = new DB_Table(); * $obj->setErrorMessage(DB_TABLE_ERR_PHPTYPE, '...'); * $obj->setErrorMessage(DB_TABLE_ERR_SQL_UNDEF, '...'); * (5) $obj = new DB_Table(); * $obj->setErrorMessage(array(DB_TABLE_ERR_PHPTYPE => '...'); * DB_TABLE_ERR_SQL_UNDEF => '...'); * * * For errors that can occur with-in the constructor call (i.e. e.g. creating * or altering the database table), only the code from examples (1) to (3) * will alter the default error messages early enough. For errors that can * occur later, examples (4) and (5) are also valid. */ $GLOBALS['_DB_TABLE']['default_error'] = array( DB_TABLE_ERR_NOT_DB_OBJECT => 'First parameter must be a DB/MDB2 object', DB_TABLE_ERR_PHPTYPE => 'DB/MDB2 phptype (or dbsyntax) not supported', DB_TABLE_ERR_SQL_UNDEF => 'Select query string not in a key of $sql. Key', DB_TABLE_ERR_SQL_NOT_STRING => 'Select query is neither an array nor a string', DB_TABLE_ERR_INS_COL_NOMAP => 'Insert column not in map', DB_TABLE_ERR_INS_COL_REQUIRED => 'Insert data must be set and non-null for column', DB_TABLE_ERR_INS_DATA_INVALID => 'Insert data not valid for column', DB_TABLE_ERR_UPD_COL_NOMAP => 'Update column not in map', DB_TABLE_ERR_UPD_COL_REQUIRED => 'Update column must be set and non-null', DB_TABLE_ERR_UPD_DATA_INVALID => 'Update data not valid for column', DB_TABLE_ERR_CREATE_FLAG => 'Create flag not valid', DB_TABLE_ERR_IDX_NO_COLS => 'No columns for index', DB_TABLE_ERR_IDX_COL_UNDEF => 'Column not in map for index', DB_TABLE_ERR_IDX_TYPE => 'Type not valid for index', DB_TABLE_ERR_DECLARE_STRING => 'String column declaration not valid', DB_TABLE_ERR_DECLARE_DECIMAL => 'Decimal column declaration not valid', DB_TABLE_ERR_DECLARE_TYPE => 'Column type not valid', DB_TABLE_ERR_VALIDATE_TYPE => 'Cannot validate for unknown type on column', DB_TABLE_ERR_DECLARE_COLNAME => 'Column name not valid', DB_TABLE_ERR_DECLARE_IDXNAME => 'Index name not valid', DB_TABLE_ERR_DECLARE_TYPE => 'Column type not valid', DB_TABLE_ERR_IDX_COL_CLOB => 'CLOB column not allowed for index', DB_TABLE_ERR_DECLARE_STRLEN => 'Column name too long, 30 char max', DB_TABLE_ERR_IDX_STRLEN => 'Index name too long, 30 char max', DB_TABLE_ERR_TABLE_STRLEN => 'Table name too long, 30 char max', DB_TABLE_ERR_SEQ_STRLEN => 'Sequence name too long, 30 char max', DB_TABLE_ERR_VER_TABLE_MISSING => 'Verification failed: table does not exist', DB_TABLE_ERR_VER_COLUMN_MISSING => 'Verification failed: column does not exist', DB_TABLE_ERR_VER_COLUMN_TYPE => 'Verification failed: wrong column type', DB_TABLE_ERR_NO_COLS => 'Column definition array may not be empty', DB_TABLE_ERR_VER_IDX_MISSING => 'Verification failed: index does not exist', DB_TABLE_ERR_VER_IDX_COL_MISSING => 'Verification failed: index does not contain all specified cols', DB_TABLE_ERR_CREATE_PHPTYPE => 'Creation mode is not supported for this phptype', DB_TABLE_ERR_DECLARE_PRIMARY => 'Only one primary key is allowed', DB_TABLE_ERR_DECLARE_PRIM_SQLITE => 'SQLite does not support primary keys', DB_TABLE_ERR_ALTER_TABLE_IMPOS => 'Alter table failed: changing the field type not possible', DB_TABLE_ERR_ALTER_INDEX_IMPOS => 'Alter table failed: changing the index/constraint not possible', DB_TABLE_ERR_AUTO_INC_COL => 'Illegal auto-increment column definition', DB_TABLE_ERR_TABLE_NAME_MISSING => 'Table name missing in constructor and class' ); // merge default and user-defined error messages if (!isset($GLOBALS['_DB_TABLE']['error'])) { $GLOBALS['_DB_TABLE']['error'] = array(); } foreach ($GLOBALS['_DB_TABLE']['default_error'] as $code => $message) { if (!array_key_exists($code, $GLOBALS['_DB_TABLE']['error'])) { $GLOBALS['_DB_TABLE']['error'][$code] = $message; } } // set default value for length check switch if (!isset($GLOBALS['_DB_TABLE']['disable_length_check'])) { $GLOBALS['_DB_TABLE']['disable_length_check'] = false; } /** * DB_Table is a database API and data type SQL abstraction class. * * DB_Table provides database API abstraction, data type abstraction, * automated SELECT, INSERT, and UPDATE queries, automated table * creation, automated validation of inserted/updated column values, * and automated creation of QuickForm elemnts based on the column * definitions. * * @category Database * @package DB_Table * @author Paul M. Jones * @author David C. Morse * @author Mark Wiesemann * @version Release: 1.5.6 * @link http://pear.php.net/package/DB_Table */ class DB_Table extends DB_Table_Base { /** * The table or view in the database to which this object binds. * * @access public * @var string */ var $table = null; /** * DB_Table_Database instance that this table belongs to. * * @access private * @var object */ var $_database = null; /** * Associative array of column definitions. * * @access public * @var array */ var $col = array(); /** * Associative array of index definitions. * * @access public * @var array */ var $idx = array(); /** * Name of an auto-increment column, if any. Null otherwise. * * A table can contain at most one auto-increment column. * Auto-incrementing is implemented in the insert() method, * using a sequence accessed by the nextID() method. * * @access public * @var string */ var $auto_inc_col = null; /** * Boolean flag to turn on (true) or off (false) auto-incrementing. * * Auto-increment column $auto_inc_col upon insertion only if $_auto_inc is * true and the value of that column is null in the data to be inserted. * * @var bool * @access private */ var $_auto_inc = true; /** * Whether or not to automatically validate data at insert-time. * * @var bool * @access private */ var $_valid_insert = true; /** * Whether or not to automatically validate data at update-time. * * @var bool * @access private */ var $_valid_update = true; /** * Whether or not to automatically recast data at insert- and update-time. * * @var bool * @access private */ var $_auto_recast = true; /** * Constructor. * * The constructor returns a DB_Table object that wraps an * instance $db DB or MDB2, and that binds to a specific database * table named $table. It can optionally create the database table * or verify that its schema matches that declared in the $col and * $idx parameters, depending on the value of the $create parameter. * * If there is an error on instantiation, $this->error will be * populated with the PEAR_Error. * * @param object &$db A PEAR DB/MDB2 object. * * @param string $table The table name to connect to in the database. * * @param mixed $create The automatic table creation mode to pursue: * - boolean false to not attempt creation * - 'safe' to create the table only if it does not exist * - 'drop' to drop any existing table with the same name and re-create it * - 'verify' to check whether the table exists, whether all the columns * exist, whether the columns have the right type, and whether the indexes * exist and have the right type * - 'alter' does the same as 'safe' if the table does not exist; if it * exists, a verification for columns existence, the column types, the * indexes existence, and the indexes types will be performed and the * table schema will be modified if needed * * @return object DB_Table * @access public */ function __construct(&$db, $table = null, $create = false) { // Identify the class for error handling by parent class $this->_primary_subclass = 'DB_TABLE'; // is the first argument a DB/MDB2 object? $this->backend = null; if (is_subclass_of($db, 'db_common')) { $this->backend = 'db'; } elseif (is_subclass_of($db, 'mdb2_driver_common')) { $this->backend = 'mdb2'; } if (is_null($this->backend)) { $this->error = DB_Table::throwError(DB_TABLE_ERR_NOT_DB_OBJECT); return; } // set the class properties $this->db =& $db; if (is_null($table)) { // $table parameter not given => check $table class property if (is_null($this->table)) { $this->error = DB_Table::throwError(DB_TABLE_ERR_TABLE_NAME_MISSING); return; } } else { $this->table = $table; } // is the RDBMS supported? $phptype = $db->phptype; $dbsyntax = $db->dbsyntax; if (! DB_Table::supported($phptype, $dbsyntax)) { $this->error =& DB_Table::throwError( DB_TABLE_ERR_PHPTYPE, "({$db->phptype})" ); return; } // load MDB2_Extended module if ($this->backend == 'mdb2') { $this->db->loadModule('Extended', null, false); } // should we attempt table creation? if ($create) { if ($this->backend == 'mdb2') { $this->db->loadModule('Manager'); } // check whether the chosen mode is supported $mode_supported = DB_Table::modeSupported($create, $phptype); if (PEAR::isError($mode_supported)) { $this->error =& $mode_supported; return; } if (!$mode_supported) { $this->error =& $this->throwError( DB_TABLE_ERR_CREATE_PHPTYPE, "('$create', '$phptype')" ); return; } include_once 'DB/Table/Manager.php'; switch ($create) { case 'alter': $result = $this->alter(); break; case 'drop': case 'safe': $result = $this->create($create); break; case 'verify': $result = $this->verify(); break; } if (PEAR::isError($result)) { // problem creating/altering/verifing the table $this->error =& $result; return; } } } /** * Is a particular RDBMS supported by DB_Table? * * @static * @param string $phptype The RDBMS type for PHP. * @param string $dbsyntax The chosen database syntax. * @return bool True if supported, false if not. * @access public */ function supported($phptype, $dbsyntax = '') { // only Firebird is supported, not its ancestor Interbase if ($phptype == 'ibase' && $dbsyntax != 'firebird') { return false; } $supported = array_keys($GLOBALS['_DB_TABLE']['type']); return in_array(strtolower($phptype), $supported); } /** * Is a creation mode supported for a RDBMS by DB_Table? * * @param string $mode The chosen creation mode. * @param string $phptype The RDBMS type for PHP. * @return bool True if supported, false if not (PEAR_Error on failure) * * @throws PEAR_Error if * Unknown creation mode is specified (DB_TABLE_ERR_CREATE_FLAG) * * @access public */ function modeSupported($mode, $phptype) { // check phptype for validity $supported = array_keys($GLOBALS['_DB_TABLE']['type']); if (!in_array(strtolower($phptype), $supported)) { return false; } switch ($mode) { case 'drop': case 'safe': // supported for all RDBMS return true; case 'alter': case 'verify': // not supported for fbsql and mssql (yet) switch ($phptype) { case 'fbsql': case 'mssql': return false; default: return true; } default: // unknown creation mode return $this->throwError( DB_TABLE_ERR_CREATE_FLAG, "('$mode')" ); } } /** * Overwrite one or more error messages, e.g. to internationalize them. * * @param mixed $code If string, the error message with code $code will * be overwritten by $message. If array, the error messages with code * of each array key will be overwritten by the key's value. * * @param string $message Only used if $key is not an array. * @return void * @access public */ function setErrorMessage($code, $message = null) { if (is_array($code)) { foreach ($code as $single_code => $single_message) { $GLOBALS['_DB_TABLE']['error'][$single_code] = $single_message; } } else { $GLOBALS['_DB_TABLE']['error'][$code] = $message; } } /** * * Returns all or part of the $this->col property array. * * @param mixed $col If null, returns the $this->col property array * as it is. If string, returns that column name from the $this->col * array. If an array, returns those columns named as the array * values from the $this->col array as an array. * * @return mixed All or part of the $this->col property array, or * boolean false if no matching column names are found. * @access public */ function getColumns($col = null) { // by default, return all column definitions if (is_null($col)) { return $this->col; } // if the param is a string, only return the column definition // named by the that string if (is_string($col)) { if (isset($this->col[$col])) { return $this->col[$col]; } else { return false; } } // if the param is a sequential array of column names, // return only those columns named in that array if (is_array($col)) { $set = array(); foreach ($col as $name) { $set[$name] = $this->getColumns($name); } if (count($set) == 0) { return false; } else { return $set; } } // param was not null, string, or array return false; } /** * Returns all or part of the $this->idx property array. * * @param mixed $idx Index name (key in $this->idx), or array of * index name strings. * * @return mixed All or part of the $this->idx property array, * or boolean false if $idx is not null but invalid * * @access public */ function getIndexes($idx = null) { // by default, return all index definitions if (is_null($idx)) { return $this->idx; } // if the param is a string, only return the index definition // named by the that string if (is_string($idx)) { if (isset($this->idx[$idx])) { return $this->idx[$idx]; } else { return false; } } // if the param is a sequential array of index names, // return only those indexes named in that array if (is_array($idx)) { $set = array(); foreach ($idx as $name) { $set[$name] = $this->getIndexes($name); } if (count($set) == 0) { return false; } else { return $set; } } // param was not null, string, or array return false; } /** * Connect or disconnect a DB_Table_Database instance to this table * instance. * * Used to re-connect this DB_Table object to a parent DB_Table_Database * object during unserialization. Can also disconnect if the $database * parameter is null. Use the DB_Table_Database::addTable method instead * to add a table to a new DB_Table_Database. * * @param object &$database DB_Table_Database instance that this table * belongs to (or null to disconnect from instance). * * @return void * @access public */ function setDatabaseInstance(&$database) { if (is_a($database, 'DB_Table_Database')) { $this->_database =& $database; } elseif (is_null($database)) { $this->_database = null; } } /** * Inserts a single table row. * * Inserts data from associative array $data, in which keys are column * names and values are column values. All required columns (except an * auto-increment column) must be included in the data array. Columns * values that are not set or null are inserted as SQL NULL values. * * If an auto-increment column is declared (by setting $this->auto_inc_col), * and the value of that column in $data is not set or null, then a new * sequence value will be generated and inserted. * * If auto-recasting is enabled (if $this->_auto_recast), the method will * try, if necessary to recast $data to proper column types, with recast(). * * If auto-validation is enabled (if $this->_valid_insert), the method * will validates column types with validInsert() before insertion. * * @access public * * @param array $data An associative array of key-value pairs where * the key is the column name and the value is the column value. * This is the data that will be inserted into the table. * * @return mixed Void on success (PEAR_Error on failure) * * @throws PEAR_Error if: * - Error in auto_inc_col declaration (DB_TABLE_ERR_AUTO_INC_COL) * - Error returned by DB/MDB2::autoExecute() (Error bubbled up) * * @see validInsert() * @see DB::autoExecute() * @see MDB2::autoExecute() */ function insert($data) { // Auto-increment if enabled and input value is null or not set if ($this->_auto_inc && !is_null($this->auto_inc_col) && !isset($data[$this->auto_inc_col]) ) { $column = $this->auto_inc_col; // check that the auto-increment column exists if (!array_key_exists($column, $this->col)) { return $this->throwError( DB_TABLE_ERR_AUTO_INC_COL, ": $column does not exist"); } // check that the column is integer if (!in_array($this->col[$column]['type'], array('integer','smallint','bigint'))) { return $this->throwError( DB_TABLE_ERR_AUTO_INC_COL, ": $column is not an integer"); } // check that the column is required // Note: The insert method will replace a null input value // of $data[$column] with a sequence value. This makes // the column effectively 'not null'. This column must be // 'required' for consistency, to make this explicit. if (!$this->isRequired($column)) { return $this->throwError( DB_TABLE_ERR_AUTO_INC_COL, ": $column is not required"); } // set the value $id = $this->nextID(); if (PEAR::isError($id)) { return $id; } $data[$column] = $id; } // forcibly recast the data elements to their proper types? if ($this->_auto_recast) { $this->recast($data); } // validate the data if auto-validation is turned on if ($this->_valid_insert) { $result = $this->validInsert($data); if (PEAR::isError($result)) { return $result; } } // Does a parent DB_Table_Database object exist? if ($this->_database) { $_database = $this->_database; // Validate foreign key values (if enabled) if ($_database->_check_fkey) { $result = $_database->validForeignKeys($this->table, $data); if (PEAR::isError($result)) { return $result; } } } // Do insertion if ($this->backend == 'mdb2') { $result = $this->db->extended->autoExecute($this->table, $data, MDB2_AUTOQUERY_INSERT); } else { $result = $this->db->autoExecute($this->table, $data, DB_AUTOQUERY_INSERT); } return $result; } /** * Turns on or off auto-incrementing of $auto_inc_col column (if any) * * For auto-incrementing to work, an $auto_inc_col column must be declared, * auto-incrementing must be enabled (by this method), and the value of * the $auto_inc_col column must be not set or null in the $data passed to * the insert method. * * @param bool $flag True to turn on auto-increment, false to turn off. * @return void * @access public */ function setAutoInc($flag = true) { if ($flag) { $this->_auto_inc = true; } else { $this->_auto_inc = false; } } /** * Turns on (or off) automatic validation of inserted data. * * Enables (if $flag is true) or disables (if $flag is false) automatic * validation of data types prior to actual insertion into the database * by the DB_Table::insert() method. * * @param bool $flag True to turn on auto-validation, false to turn off. * @return void * @access public */ function autoValidInsert($flag = true) { if ($flag) { $this->_valid_insert = true; } else { $this->_valid_insert = false; } } /** * Validates an array for insertion into the table. * * @param array $data An associative array of key-value pairs where * the key is the column name and the value is the column value. This * is the data that will be inserted into the table. Data is checked * against the column data type for validity. * * @return boolean true on success (PEAR_Error on failure) * * @throws PEAR_Error if: * - Invalid column name key in $data (DB_TABLE_ERR_INS_COL_NOMAP) * - Missing required column value (DB_TABLE_ERR_INS_COL_NOMAP) * - Column value doesn't match type (DB_TABLE_ERR_INS_DATA_INVALID) * * @access public * * @see insert() */ function validInsert(&$data) { // loop through the data, and disallow insertion of unmapped // columns foreach ($data as $col => $val) { if (! isset($this->col[$col])) { return $this->throwError( DB_TABLE_ERR_INS_COL_NOMAP, "('$col')" ); } } // loop through each column mapping, and check the data to be // inserted into it against the column data type. we loop through // column mappings instead of the insert data to make sure that // all necessary columns are being inserted. foreach ($this->col as $col => $val) { // is the value allowed to be null? if (isset($val['require']) && $val['require'] == true && (! isset($data[$col]) || is_null($data[$col]))) { return $this->throwError( DB_TABLE_ERR_INS_COL_REQUIRED, "'$col'" ); } // does the value to be inserted match the column data type? if (isset($data[$col]) && ! $this->isValid($data[$col], $col)) { return $this->throwError( DB_TABLE_ERR_INS_DATA_INVALID, "'$col' ('$data[$col]')" ); } } return true; } /** * Update table row or rows that match a custom WHERE clause * * Constructs and submits an SQL UPDATE command to update columns whose * names are keys in the $data array parameter, in all rows that match * the logical condition given by the $where string parameter. * * If auto-recasting is enabled (if $this->_auto_recast), update() will * try, if necessary, to recast $data to proper column types, with recast(). * * If auto-validation is enabled (if $this->_valid_insert), update() * validates column types with validUpdate() before insertion. * * @param array $data An associative array of key-value pairs where the * key is the column name and the value is the column value. These are * the columns that will be updated with new values. * * @param string $where An SQL WHERE clause limiting which records are * are to be updated. * * @return mixed Void on success, a PEAR_Error object on failure. * * @throws PEAR_Error if: * - Data fails type validation (bubbles error returned by validUpdate) * - Error thrown by DB/MDB2::autoexecute() * * @access public * * @see validUpdate() * @see DB::autoExecute() * @see MDB2::autoExecute() */ function update($data, $where) { // forcibly recast the data elements to their proper types? if ($this->_auto_recast) { $this->recast($data); } // validate the data if auto-validation is turned on if ($this->_valid_update) { $result = $this->validUpdate($data); if (PEAR::isError($result)) { return $result; } } // Does a parent DB_Table_Database object exist? if ($this->_database) { $_database =& $this->_database; // Validate foreign key values (if enabled) if ($_database->_check_fkey) { $result = $_database->validForeignKeys($this->table, $data); if (PEAR::isError($result)) { return $result; } } // Implement any relevant ON UPDATE actions $result = $_database->onUpdateAction($this, $data, $where); if (PEAR::isError($result)) { return $result; } } // Submit update command if ($this->backend == 'mdb2') { $result = $this->db->extended->autoExecute($this->table, $data, MDB2_AUTOQUERY_UPDATE, $where); } else { $result = $this->db->autoExecute($this->table, $data, DB_AUTOQUERY_UPDATE, $where); } return $result; } /** * Turns on (or off) automatic validation of updated data. * * Enables (if $flag is true) or disables (if $flag is false) automatic * validation of data types prior to updating rows in the database by * the {@link update()} method. * * @param bool $flag True to turn on auto-validation, false to turn off. * @return void * @access public */ function autoValidUpdate($flag = true) { if ($flag) { $this->_valid_update = true; } else { $this->_valid_update = false; } } /** * Validates an array for updating the table. * * @param array $data An associative array of key-value pairs where * the key is the column name and the value is the column value. This * is the data that will be inserted into the table. Data is checked * against the column data type for validity. * * @return mixed Boolean true on success (PEAR_Error object on failure) * * @throws PEAR_Error if * - Invalid column name key in $data (DB_TABLE_ERR_UPD_COL_NOMAP) * - Missing required column value (DB_TABLE_ERR_UPD_COL_NOMAP) * - Column value doesn't match type (DB_TABLE_ERR_UPD_DATA_INVALID) * * @access public * * @see update() */ function validUpdate(&$data) { // loop through each data element, and check the // data to be updated against the column data type. foreach ($data as $col => $val) { // does the column exist? if (! isset($this->col[$col])) { return $this->throwError( DB_TABLE_ERR_UPD_COL_NOMAP, "('$col')" ); } // the column definition $defn = $this->col[$col]; // is it allowed to be null? if (isset($defn['require']) && $defn['require'] == true && isset($data[$col]) && is_null($data[$col])) { return $this->throwError( DB_TABLE_ERR_UPD_COL_REQUIRED, $col ); } // does the value to be inserted match the column data type? if (! $this->isValid($data[$col], $col)) { return $this->throwError( DB_TABLE_ERR_UPD_DATA_INVALID, "$col ('$data[$col]')" ); } } return true; } /** * Deletes table rows matching a custom WHERE clause. * * Constructs and submits and SQL DELETE command with the specified WHERE * clause. Command is submitted by DB::query() or MDB2::exec(). * * If a reference to a DB_Table_Database instance exists, carry out any * ON DELETE actions declared in that instance before actual insertion, * if emulation of ON DELETE actions is enabled in that instance. * * @param string $where Logical condition in the WHERE clause of the * delete command. * * @return mixed void on success (PEAR_Error on failure) * * @throws PEAR_Error if * DB::query() or MDB2::exec() returns error (bubbles up) * * @access public * * @see DB::query() * @see MDB2::exec() */ function delete($where) { // Does a parent DB_Table_Database object exist? if ($this->_database) { $_database =& $this->_database; // Implement any relevant ON DELETE actions $result = $_database->onDeleteAction($this, $where); if (PEAR::isError($result)) { return $result; } } if ($this->backend == 'mdb2') { $result = $this->db->exec("DELETE FROM $this->table WHERE $where"); } else { $result = $this->db->query("DELETE FROM $this->table WHERE $where"); } return $result; } /** * * Generates and returns a sequence value. * * Generates a sequence value by calling the DB or MDB2::nextID() method. The * sequence name defaults to the table name, or may be specified explicitly. * * @param string $seq_name The sequence name; defaults to table_id. * * @return integer The next value in the sequence (PEAR_Error on failure) * * @throws PEAR_Error if * Sequence name too long (>26 char + _seq) (DB_TABLE_ERR_SEQ_STRLEN) * * @access public * * @see DB::nextID() * @see MDB2::nextID() */ function nextID($seq_name = null) { if (is_null($seq_name)) { $seq_name = "{$this->table}"; } else { $seq_name = "{$this->table}_{$seq_name}"; } // the maximum length is 30, but PEAR DB/MDB2 will add "_seq" to the // name, so the max length here is less 4 chars. we have to // check here because the sequence will be created automatically // by PEAR DB/MDB2, which will not check for length on its own. if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false && strlen($seq_name) > 26 ) { return DB_Table::throwError( DB_TABLE_ERR_SEQ_STRLEN, " ('$seq_name')" ); } return $this->db->nextId($seq_name); } /** * Escapes and enquotes a value for use in an SQL query. * * Simple wrapper for DB_Common::quoteSmart() or MDB2::quote(), which * returns the value of one of these functions. Helps makes user input * safe against SQL injection attack. * * @param mixed $val The value to be quoted * * @return string The value with quotes escaped, inside single quotes if * non-numeric. * * @throws PEAR_Error if * DB_Common::quoteSmart() or MDB2::quote() returns Error (bubbled up) * * @access public * * @see DB_Common::quoteSmart() * @see MDB2::quote() */ function quote($val) { if ($this->backend == 'mdb2') { $val = $this->db->quote($val); } else { $val = $this->db->quoteSmart($val); } return $val; } /** * Returns a blank row array based on the column map. * * The array keys are the column names, and all values are set to null. * * @return array An associative array where keys are column names and * all values are null. * @access public */ function getBlankRow() { $row = array(); foreach ($this->col as $key => $val) { $row[$key] = null; } $this->recast($row); return $row; } /** * Turns on (or off) automatic recasting of insert and update data. * * Turns on (if $flag is true) or off (if $flag is false) automatic forcible * recasting of data to the declared data type, if required, prior to inserting * or updating. The recasting is done by calling the DB_Table::recast() * method from within the DB_Table::insert() and DB_Table::update(). * * @param bool $flag True to automatically recast insert and update data, * false to not do so. * @return void * @access public */ function autoRecast($flag = true) { if ($flag) { $this->_auto_recast = true; } else { $this->_auto_recast = false; } } /** * Forces array elements to the proper types for their columns. * * This will not valiate the data, and will forcibly change the data * to match the recast-type. * * The date, time, and timestamp recasting has special logic for * arrays coming from an HTML_QuickForm object so that the arrays * are converted into properly-formatted strings. * * @todo If a column key holds an array of values (say from a multiple * select) then this method will not work properly; it will recast the * value to the string 'Array'. Is this bad? * * @param array &$data The data array to re-cast. * * @return void * * @access public */ function recast(&$data) { $keys = array_keys($data); $null_if_blank = array('date', 'time', 'timestamp', 'smallint', 'integer', 'bigint', 'decimal', 'single', 'double'); foreach ($keys as $key) { if (! isset($this->col[$key])) { continue; } unset($val); $val =& $data[$key]; // convert blanks to null for non-character field types $convert = in_array($this->col[$key]['type'], $null_if_blank); if (is_array($val)) { // if one of the given array values is // empty, null will be the new value if // the field is not required $tmp_val = implode('', $val); foreach ($val as $array_val) { if (trim((string) $array_val) == '') { $tmp_val = ''; break; } } } else { $tmp_val = $val; } if ($convert && trim((string) $tmp_val) == '' && ( !isset($this->col[$key]['require']) || $this->col[$key]['require'] === false ) ) { $val = null; } // skip explicit NULL values if (is_null($val)) { continue; } // otherwise, recast to the column type switch ($this->col[$key]['type']) { case 'boolean': $val = ($val) ? 1 : 0; break; case 'char': case 'varchar': case 'clob': settype($val, 'string'); break; case 'date': // smart handling of non-standard (i.e. Y-m-d) date formats, // this allows to use two-digit years (y) and short (M) or // long (F) names of months without having to recast the // date value yourself if (is_array($val)) { if (isset($val['y'])) { $val['Y'] = $val['y']; } if (isset($val['F'])) { $val['m'] = $val['F']; } if (isset($val['M'])) { $val['m'] = $val['M']; } } if (is_array($val) && isset($val['Y']) && isset($val['m']) && isset($val['d'])) { // the date is in HTML_QuickForm format, // convert into a string $y = (strlen($val['Y']) < 4) ? str_pad($val['Y'], 4, '0', STR_PAD_LEFT) : $val['Y']; $m = (strlen($val['m']) < 2) ? '0'.$val['m'] : $val['m']; $d = (strlen($val['d']) < 2) ? '0'.$val['d'] : $val['d']; $val = "$y-$m-$d"; } else { // convert using the Date class $tmp = new DB_Table_Date($val); $val = $tmp->format('%Y-%m-%d'); } break; case 'time': if (is_array($val) && isset($val['H']) && isset($val['i']) && isset($val['s'])) { // the time is in HTML_QuickForm format, // convert into a string $h = (strlen($val['H']) < 2) ? '0' . $val['H'] : $val['H']; $i = (strlen($val['i']) < 2) ? '0' . $val['i'] : $val['i']; $s = (strlen($val['s']) < 2) ? '0' . $val['s'] : $val['s']; $val = "$h:$i:$s"; } else { // date does not matter in this case, so // pre 1970 and post 2040 are not an issue. $tmp = strtotime(date('Y-m-d') . " $val"); $val = date('H:i:s', $tmp); } break; case 'timestamp': // smart handling of non-standard (i.e. Y-m-d) date formats, // this allows to use two-digit years (y) and short (M) or // long (F) names of months without having to recast the // date value yourself if (is_array($val)) { if (isset($val['y'])) { $val['Y'] = $val['y']; } if (isset($val['F'])) { $val['m'] = $val['F']; } if (isset($val['M'])) { $val['m'] = $val['M']; } } if (is_array($val) && isset($val['Y']) && isset($val['m']) && isset($val['d']) && isset($val['H']) && isset($val['i']) && isset($val['s'])) { // timestamp is in HTML_QuickForm format, // convert each element to a string. pad // with zeroes as needed. $y = (strlen($val['Y']) < 4) ? str_pad($val['Y'], 4, '0', STR_PAD_LEFT) : $val['Y']; $m = (strlen($val['m']) < 2) ? '0'.$val['m'] : $val['m']; $d = (strlen($val['d']) < 2) ? '0'.$val['d'] : $val['d']; $h = (strlen($val['H']) < 2) ? '0' . $val['H'] : $val['H']; $i = (strlen($val['i']) < 2) ? '0' . $val['i'] : $val['i']; $s = (strlen($val['s']) < 2) ? '0' . $val['s'] : $val['s']; $val = "$y-$m-$d $h:$i:$s"; } else { // convert using the Date class $tmp = new DB_Table_Date($val); $val = $tmp->format('%Y-%m-%d %H:%M:%S'); } break; case 'smallint': case 'integer': case 'bigint': settype($val, 'integer'); break; case 'decimal': case 'single': case 'double': settype($val, 'float'); break; } } } /** * Creates the table based on $this->col and $this->idx. * * @param string $flag The automatic table creation mode to pursue: * - 'safe' to create the table only if it does not exist * - 'drop' to drop any existing table with the same name and re-create it * * @return mixed Boolean true if the table was successfully created, * false if there was no need to create the table, or * a PEAR_Error if the attempted creation failed. * * @throws PEAR_Error if * - DB_Table_Manager::tableExists() returns Error (bubbles up) * - DB_Table_Manager::create() returns Error (bubbles up) * * @access public * * @see DB_Table_Manager::tableExists() * @see DB_Table_Manager::create() */ function create($flag) { include_once 'DB/Table/Manager.php'; // are we OK to create the table? $ok = false; // check the create-flag switch ($flag) { case 'drop': // drop only if table exists $table_exists = DB_Table_Manager::tableExists($this->db, $this->table); if (PEAR::isError($table_exists)) { return $table_exists; } if ($table_exists) { // forcibly drop an existing table if ($this->backend == 'mdb2') { $this->db->manager->dropTable($this->table); } else { $this->db->query("DROP TABLE {$this->table}"); } } $ok = true; break; case 'safe': // create only if table does not exist $table_exists = DB_Table_Manager::tableExists($this->db, $this->table); if (PEAR::isError($table_exists)) { return $table_exists; } // ok to create only if table does not exist $ok = !$table_exists; break; } // are we going to create the table? if (! $ok) { return false; } return DB_Table_Manager::create( $this->db, $this->table, $this->col, $this->idx ); } /** * Alters the table to match schema declared in $this->col and $this->idx. * * If the table does not exist, create it instead. * * @return boolean true if altering is successful (PEAR_Error on failure) * * @throws PEAR_Error if * - DB_Table_Manager::tableExists() returns Error (bubbles up) * - DB_Table_Manager::create() returns Error (bubbles up) * - DB_Table_Manager::alter() returns Error (bubbles up) * * @access public * * @see DB_Table_Manager::tableExists() * @see DB_Table_Manager::create() * @see DB_Table_Manager::alter() */ function alter() { $create = false; // alter the table columns and indexes if the table exists $table_exists = DB_Table_Manager::tableExists($this->db, $this->table); if (PEAR::isError($table_exists)) { return $table_exists; } if (!$table_exists) { // table does not exist => just create the table, there is // nothing that could be altered $create = true; } if ($create) { return DB_Table_Manager::create( $this->db, $this->table, $this->col, $this->idx ); } return DB_Table_Manager::alter( $this->db, $this->table, $this->col, $this->idx ); } /** * Verifies the table based on $this->col and $this->idx. * * @return boolean true if verification succees (PEAR_Error on failure). * * @throws PEAR_Error if * DB_Table_Manager::verify() returns Error (bubbles up) * * @access public * * @see DB_Table_Manager::verify() */ function verify() { return DB_Table_Manager::verify( $this->db, $this->table, $this->col, $this->idx ); } /** * Checks if a value validates against the DB_Table data type for a * given column. This only checks that it matches the data type; it * does not do extended validation. * * @param array $val A value to check against the column's DB_Table * data type. * * @param array $col A column name from $this->col. * * @return boolean True if $val validates against data type, false if not * * @throws PEAR_Error if * Invalid column type in $this->col (DB_TABLE_ERR_VALIDATE_TYPE) * * @access public * * @see DB_Table_Valid */ function isValid($val, $col) { // is the value null? if (is_null($val)) { // is the column required? if ($this->isRequired($col)) { // yes, so not valid return false; } else { // not required, so it's valid return true; } } // make sure we have the validation class include_once 'DB/Table/Valid.php'; // validate values per the column type. we use sqlite // as the single authentic list of allowed column types, // regardless of the actual rdbms being used. $map = array_keys($GLOBALS['_DB_TABLE']['type']['sqlite']); // is the column type on the map? if (! in_array($this->col[$col]['type'], $map)) { return $this->throwError( DB_TABLE_ERR_VALIDATE_TYPE, "'$col' ('{$this->col[$col]['type']}')" ); } // validate for the type switch ($this->col[$col]['type']) { case 'char': case 'varchar': $result = DB_Table_Valid::isChar( $val, $this->col[$col]['size'] ); break; case 'decimal': $result = DB_Table_Valid::isDecimal( $val, $this->col[$col]['size'], $this->col[$col]['scope'] ); break; default: $result = call_user_func( array( 'DB_Table_Valid', 'is' . ucwords($this->col[$col]['type']) ), $val ); break; } // have we passed the check so far, and should we // also check for allowed values? if ($result && isset($this->col[$col]['qf_vals'])) { $keys = array_keys($this->col[$col]['qf_vals']); $result = in_array( $val, array_keys($this->col[$col]['qf_vals']) ); } return $result; } /** * Is a specific column required to be set and non-null? * * @param mixed $column The column to check against. * @return boolean True if required, false if not. * @access public */ function isRequired($column) { if (isset($this->col[$column]['require']) && $this->col[$column]['require'] == true) { return true; } else { return false; } } /** * * Creates and returns a QuickForm object based on table columns. * * @param array $columns A sequential array of column names to use in * the form; if null, uses all columns. * * @param string $array_name By default, the form will use the names * of the columns as the names of the form elements. If you pass * $array_name, the column names will become keys in an array named * for this parameter. * * @param array $args An associative array of optional arguments to * pass to the QuickForm object. The keys are... * * 'formName' : String, name of the form; defaults to the name of this * table. * * 'method' : String, form method; defaults to 'post'. * * 'action' : String, form action; defaults to * $_SERVER['REQUEST_URI']. * * 'target' : String, form target target; defaults to '_self' * * 'attributes' : Associative array, extra attributes for
* tag; the key is the attribute name and the value is attribute * value. * * 'trackSubmit' : Boolean, whether to track if the form was * submitted by adding a special hidden field * * @param string $clientValidate By default, validation will match * the 'qf_client' value from the column definition. However, if * you set $clientValidate to true or false, this will override the * value from the column definition. * * @param array $formFilters An array with filter function names or * callbacks that will be applied to all form elements. * * @return object HTML_QuickForm * * @access public * * @see HTML_QuickForm * @see DB_Table_QuickForm */ function &getForm($columns = null, $array_name = null, $args = array(), $clientValidate = null, $formFilters = null) { include_once 'DB/Table/QuickForm.php'; $coldefs = $this->_getFormColDefs($columns); $form =& DB_Table_QuickForm::getForm($coldefs, $array_name, $args, $clientValidate, $formFilters); return $form; } /** * Adds elements and rules to a pre-existing HTML_QuickForm object. * * By default, the form will use the names of the columns as the names * of the form elements. If you pass $array_name, the column names * will become keys in an array named for this parameter. * * @param object &$form An HTML_QuickForm object. * * @param array $columns A sequential array of column names to use in * the form; if null, uses all columns. * * @param string $array_name Name of array of column names * * @param clientValidate * * @return void * * @access public * * @see HTML_QuickForm * * @see DB_Table_QuickForm */ function addFormElements(&$form, $columns = null, $array_name = null, $clientValidate = null) { include_once 'DB/Table/QuickForm.php'; $coldefs = $this->_getFormColDefs($columns); DB_Table_QuickForm::addElements($form, $coldefs, $array_name); DB_Table_QuickForm::addRules($form, $coldefs, $array_name, $clientValidate); } /** * Adds static form elements like 'header', 'static', 'submit' or 'reset' * to a pre-existing HTML_QuickForm object. The form elements needs to be * defined in a property called $frm. * * @param object &$form An HTML_QuickForm object. * @return void * @access public * * @see HTML_QuickForm * @see DB_Table_QuickForm */ function addStaticFormElements(&$form) { include_once 'DB/Table/QuickForm.php'; DB_Table_QuickForm::addStaticElements($form, $this->frm); } /** * * Creates and returns an array of QuickForm elements based on an * array of DB_Table column names. * * @param array $columns A sequential array of column names to use in * the form; if null, uses all columns. * * @param string $array_name By default, the form will use the names * of the columns as the names of the form elements. If you pass * $array_name, the column names will become keys in an array named * for this parameter. * * @return array An array of HTML_QuickForm_Element objects. * * @access public * * @see HTML_QuickForm * @see DB_Table_QuickForm */ function &getFormGroup($columns = null, $array_name = null) { include_once 'DB/Table/QuickForm.php'; $coldefs = $this->_getFormColDefs($columns); $group = DB_Table_QuickForm::getGroup($coldefs, $array_name); return $group; } /** * Creates and returns a single QuickForm element based on a DB_Table * column name. * * @param string $column A DB_Table column name. * @param string $elemname The name to use for the generated QuickForm * element. * * @return object HTML_QuickForm_Element * * @access public * * @see HTML_QuickForm * @see DB_Table_QuickForm */ function &getFormElement($column, $elemname) { include_once 'DB/Table/QuickForm.php'; $coldef = $this->_getFormColDefs($column); DB_Table_QuickForm::fixColDef($coldef[$column], $elemname); $element =& DB_Table_QuickForm::getElement($coldef[$column], $elemname); return $element; } /** * Creates and returns an array of QuickForm elements based on a DB_Table * column name. * * @author Ian Eure * * @param array $cols Array of DB_Table column names * @param string $array_name The name to use for the generated QuickForm * elements. * @return object HTML_QuickForm_Element * * @access public * * @see HTML_QuickForm * @see DB_Table_QuickForm */ function &getFormElements($cols, $array_name = null) { include_once 'DB/Table/QuickForm.php'; $elements = DB_Table_QuickForm::getElements($cols, $array_name); return $elements; } /** * Creates a column definition array suitable for DB_Table_QuickForm. * * @param string|array $column_set A string column name, a sequential * array of columns names, or an associative array where the key is a * column name and the value is the default value for the generated * form element. If null, uses all columns for this class. * * @return array An array of column defintions suitable for passing * to DB_Table_QuickForm. * * @access public * */ function _getFormColDefs($column_set = null) { if (is_null($column_set)) { // no columns or columns+values; just return the $this->col // array. return $this->getColumns($column_set); } // check to see if the keys are sequential integers. if so, // the $column_set is just a list of columns. settype($column_set, 'array'); $keys = array_keys($column_set); $all_integer = true; foreach ($keys as $val) { if (! is_integer($val)) { $all_integer = false; break; } } if ($all_integer) { // the column_set is just a list of columns; get back the $this->col // array elements matching this list. $coldefs = $this->getColumns($column_set); } else { // the columns_set is an associative array where the key is a // column name and the value is the form element value. $coldefs = $this->getColumns($keys); foreach ($coldefs as $key => $val) { $coldefs[$key]['qf_setvalue'] = $column_set[$key]; } } return $coldefs; } /** * Returns XML string representation of the table * * @param string $indent string of whitespace * @return string XML string * @access public */ function toXML($indent = '') { require_once 'DB/Table/XML.php'; $s = array(); $s[] = DB_Table_XML::openTag('table', $indent); $s[] = DB_Table_XML::lineElement('name', $this->table, $indent); $s[] = DB_Table_XML::openTag('declaration', $indent); // Column declarations foreach ($this->col as $name => $col) { $type = (isset($col['type'])) ? $col['type'] : null; $size = (isset($col['size'])) ? $col['size'] : null; $scope = (isset($col['scope'])) ? $col['scope'] : null; $require = (isset($col['require'])) ? $col['require'] : null; $default = (isset($col['set default'])) ? $col['set default'] : null; $line = ' ' . $name . ' ' . $type; $s[] = DB_Table_XML::openTag('field', $indent); $s[] = DB_Table_XML::lineElement('name', $name, $indent); $s[] = DB_Table_XML::lineElement('type', $type, $indent); if ($size) { $s[] = DB_Table_XML::lineElement('length', $size, $indent); } if ($require) { $require = (int) $require; $s[] = DB_Table_XML::lineElement('notnull', $require, $indent); } if (!($default === null)) { $s[] = DB_Table_XML::lineElement('set default', $default, $indent); } if ($this->auto_inc_col == $name) { $s[] = DB_Table_XML::lineElement('autoincrement', '1', $indent); } $s[] = DB_Table_XML::closeTag('field', $indent); } // Index declarations foreach ($this->idx as $name => $idx) { $s[] = DB_Table_XML::openTag('index', $indent); $cols = $idx['cols']; $type = $idx['type']; if (is_string($name)) { $s[] = DB_Table_XML::lineElement('name', $name, $indent); } if ($type == 'primary') { $s[] = DB_Table_XML::lineElement('primary', '1', $indent); } elseif ($type == 'unique') { $s[] = DB_Table_XML::lineElement('unique', '1', $indent); } if (is_string($cols)) { $cols = array($cols); } foreach ($cols as $col) { $s[] = DB_Table_XML::lineElement('field', $col, $indent); } $s[] = DB_Table_XML::closeTag('index', $indent); } // Foreign key references (if $this->_database is not null) if ($this->_database) { if (isset($this->_database->_ref[$this->table])) { $refs = $this->_database->_ref[$this->table]; foreach ($refs as $rtable => $def) { $fkey = $def['fkey']; // foreign key of referencing table $rkey = $def['rkey']; // referenced/primary key if (is_string($fkey)) { $fkey = array($fkey); } if (is_string($rkey)) { $rkey = array($rkey); } $on_delete = $def['on_delete']; // on-delete action $on_update = $def['on_update']; // on-update action $s[] = DB_Table_XML::openTag('foreign', $indent); foreach ($fkey as $fcol) { $s[] = DB_Table_XML::lineElement('field', $fcol, $indent); } $s[] = DB_Table_XML::openTag('references', $indent); $s[] = DB_Table_XML::lineElement('table', $rtable, $indent); if ($rkey) { foreach ($rkey as $rcol) { $s[] = DB_Table_XML::lineElement('field', $rcol, $indent); } } $s[] = DB_Table_XML::closeTag('references', $indent); if ($on_delete) { $s[] = DB_Table_XML::lineElement('ondelete', $on_delete, $indent); } if ($on_update) { $s[] = DB_Table_XML::lineElement('onupdate', $on_update, $indent); } $s[] = DB_Table_XML::closeTag('foreign', $indent); } } } $s[] = DB_Table_XML::closeTag('declaration', $indent); $s[] = DB_Table_XML::closeTag('table', $indent); return implode("\n", $s); } } ?>