* @copyright  2001-2006 VIKO team and contributors
 * @license    http://www.gnu.org/licenses/gpl.html  GPL 2.0
 */
/**
 * This class abstracts database tables, so it needs database connection
 */
require_once 'DBInstance.php';
/**
 * Handles general tasks of interfacing a database table
 *
 * Subclasses need to implement the specific functionality.
 * Child classes defenetly need to override the following methods:
 *
 * 
 * - readFromDatabaseRecord()*
- writeToDatabaseRecord()*
- getTableName()*
- getIDFieldName()*
*
 * @abstract
 */
class TableAbstraction
{
    /**
     * Value of the ID column
     *
     * TableAbstraction class requires that the table has one column,
     * that contains the unique primary key (possibly auto-generated).
     *
     * @access private
     * @var integer
     */
    var $_id = null;
    /**
     * Creates new TableAbstraction instance
     *
     * The record ID may be optionally specified.
     *
     * @param int $id database record ID
     */
    function TableAbstraction( $id = null ) {
        $this->_id = $id;
    }
    /**
     * Returns the value of ID column, null if unset
     *
     * @access public
     * @return integer row ID
     */
    function getID()
    {
        return $this->_id;
    }
    /**
     * Sets value for the ID column
     *
     * @access public
     * @param integer $id row ID
     */
    function setID( $id )
    {
        $this->_id = $id;
    }
    /**
     * Returns the value of private attribute
     *
     * If attribute is not set, then it is attempted to retrieve from database.
     * If the retrieval from database failed, false is returned.
     *
     * @access protected
     * @param string $attribute_name
     * @return mixed value of the requested attribute
     */
    function getAttribute( $attribute_name )
    {
        if ( isset($this->$attribute_name) ) {
            // the requested value is readily available - just return it
            return $this->$attribute_name;
        }
        else {
            // so we request it from the database
            if ( $this->loadFromDatabaseByID() ) {
                return ( isset($this->$attribute_name) ) ? $this->$attribute_name : null;
            }
            else {
                return false;
            }
        }
    }
    /**
     * Loads object with info from table corresponding to specified record ID
     *
     * If a record corresponding to the ID was not found, false is returned.
     *
     * If record ID is unavailable,
     * an error is generated and the execution stops.
     *
     * @access public
     * @return bool true if corresponding record was found
     */
    function loadFromDatabaseByID()
    {
        // ensure that record ID is available
        if ( !isset($this->_id) ) {
            trigger_error( "No record ID specified when loading.", E_USER_ERROR );
        }
        // try to load a DB record corresponding to the ID
        $db = DBInstance::get();
        $table_name = $this->getTableName();
        $id_field_name = $this->getIDFieldName();
        $record = $db->getRow(
            "SELECT
                *
            FROM
                $table_name
            WHERE
                $id_field_name = ?
            LIMIT 1",
            array( $this->_id )
        );
        // Ensure that the query went without error.
        // If it didn't announce the error and stop execution.
        if ( PEAR::isError( $record ) ) {
            trigger_error( $record->getMessage(), E_USER_ERROR );
        }
        // if the ID existed in table apply the retrieved data to this object.
        // if not, then just return false.
        if ( isset($record) ) {
            $this->readFromDatabaseRecord( $record );
            return true;
        }
        else {
            return false;
        }
    }
    /**
     * Saves object information into database
     *
     * If record ID is set, an existing record will be updated,
     * without ID a new record will be inserted.
     *
     * @access public
     * @return boolean success
     */
    function save()
    {
        if ( isset( $this->_id ) ) {
            return $this->_updateRecord();
        }
        else {
            return $this->_insertRecord();
        }
    }
    /**
     * Updates the record in database
     *
     * @access private
     * @return boolean success
     */
    function _updateRecord()
    {
        $field_values = $this->writeToDatabaseRecord();
        $where_clause = $this->getIDFieldName() . '=' . $this->_id;
        $db = DBInstance::get();
        $result = $db->autoExecute(
            $this->getTableName(),
            $field_values,
            DB_AUTOQUERY_UPDATE,
            $where_clause
        );
        // check for errors
        if ( PEAR::isError($result) ) {
            // The update might fail because of a UNIQUE constraint
            // in that case return false,
            // for other errors just show the error message and stop.
            if ( $result->getCode() == DB_ERROR_CONSTRAINT ) {
                return false;
            }
            else {
                trigger_error( $result->getMessage(), E_USER_ERROR );
            }
        }
        // we reached here, so everything should have gone just fine
        return true;
    }
    /**
     * Inserts new record into database
     *
     * @access private
     * @return boolean success
     */
    function _insertRecord()
    {
        $field_values = $this->writeToDatabaseRecord();
        $db = DBInstance::get();
        $result = $db->autoExecute(
            $this->getTableName(),
            $field_values,
            DB_AUTOQUERY_INSERT
        );
        // check for errors
        if ( PEAR::isError($result) ) {
            // The insert might fail because of a UNIQUE constraint
            // in that case return false,
            // for other errors just show the error message and stop.
            if ( $result->getCode() == DB_ERROR_CONSTRAINT ) {
                return false;
            }
            else {
                trigger_error( $result->getMessage(), E_USER_ERROR );
            }
        }
        // Get the ID of last insert with a help of MySQL specific function
        // TODO: This has to be changed, if we want to port VIKO to other databases
        $this->_id = mysql_insert_id();
        // we reached here, so everything should have gone just fine
        return true;
    }
    /**
     * Removes record corresponding to the ID from database table
     *
     * The ID must be specified for this to work.
     *
     * @access public
     * @return boolean true on successful delete.
     */
    function delete()
    {
        // ensure that record ID is available
        if ( !isset($this->_id) ) {
            trigger_error( "No record ID specified when deleting.", E_USER_ERROR );
        }
        // remove the record from database
        $db = DBInstance::get();
        $table_name = $this->getTableName();
        $id_field_name = $this->getIDFieldName();
        $result = $db->query(
            "DELETE FROM $table_name WHERE $id_field_name = ? LIMIT 1",
            $this->_id
        );
        // Check tha there were no errors in performing the query
        if ( PEAR::isError($result) ) {
            trigger_error( $result->getMessage(), E_USER_ERROR );
        }
        // return true, if a record was deleted
        return ( $db->affectedRows() == 1 );
    }
    //
    // abstract methods that require implementing in child classes
    //
    /**
     * Returns the name of the database table which the class abstracts
     *
     * @access protected
     * @return string table name
     */
    function getTableName()
    {
        trigger_error( "TableAbstraction::getTableName() not implemented.", E_USER_ERROR );
    }
    /**
     * Returns the name of the id field in database table
     *
     * @access protected
     * @return string ID field name
     */
    function getIDFieldName()
    {
        trigger_error( "TableAbstraction::getIDFieldName() not implemented.", E_USER_ERROR );
    }
    /**
     * Takes values from database record and applies those to object attributes
     *
     * @access protected
     * @param array $record a row retrieved from database
     */
    function readFromDatabaseRecord( $record )
    {
        trigger_error( "TableAbstraction::readFromDatabaseRecord() not implemented.", E_USER_ERROR );
    }
    /**
     * Reads values from object attributes into array of fields and returns it
     *
     * @access protected
     * @return array a row to be written in database
     */
    function writeToDatabaseRecord()
    {
        trigger_error( "TableAbstraction::writeToDatabaseRecord() not implemented.", E_USER_ERROR );
    }
}
?>