<?php

require_once 'common/Object.php';
require_once 'orm/OrmIDb.php';

/**
 * This is an abstract base class to make easier your implementation of a 
 * custom  database class for OrmIDb.
 * 
 * This implementation aims to help, but it's not mandatory to use or extend 
 * this class in any way.
 * This class implements both <em>getCalculatedPK</em> and <em>getCreatedPK</em> 
 * as returning null values. So, you need to implement at least one to get 
 * PK generation working.
 * This class handles transactions using an internal <em>transactionBlock</em> 
 * as an integer counter. So, it's increased each time a transaction begins, 
 * and decreased when finished, automatically from basic transaction handler 
 * methods.
 * 
 * @abstract
 * @author  $Author: pruna $
 * @package orm
 * @version $Revision: 1.3 $
 * @created 19-Apr-2006
 * @modified $Date: 2005/12/06 02:56:45 $
 */
abstract class OrmBaseDb extends Object implements OrmIDb {
    
    /**
     * Internal holder for database connection.
     *
     * @var      object
     * @access   protected
     */
    protected $conn = null;
    
    /**
     * Internal holder for DSN string.
     * This is the key element of this Database object. Setting it implies
     * creating a database connection to the specified DSN.
     *
     * @var      string
     * @access   protected
     */
    protected $dsn;
    
    /**
     * Flag that identifies the transaction that is in progress.
     * A not null value implies that a transaction is in progress. You can 
     * use any type you want, here is implemented with integers.
     *
     * @var      mixed
     * @access   protected
     */
    protected $transactionBlock = FALSE;
    
    /**
     * Internal holder for database result set.
     *
     * @var      object
     * @access   protected
     */
    protected $rs;
    
    /**
     * Constructor. If receives a new DSN, it creates a database connection.
     *
     * @access  public
     * @param   Dsn     $dsn        DSN info for database connection
     */
    public function __construct($dsn = null) {
        $this->setDsn($dsn);
    }
    
    /**
     * Ends the specified transaction block.
     * 
     * If it's not the last opened, throws an OrmException.
     * 
     * @access  protected
     * @param   mixed     $blockId     Transaction block identifier
     * @return  void
     */
    protected function closeTransactionBlock($blockId = null) {
        if (!is_null($blockId)) {
            if ($blockId !== $this->getTransactionBlock()) {
                throw new OrmException('Closing different transaction block ', 
                        OrmException::CODE_DB_TRANSACTION_MISMATCH, $this);
            }
            $this->transactionBlock -= 1;
            if ($this->transactionBlock === 0) {
                $this->transactionBlock = null;
            }
        }
    }
    
    /**
     * Commits a database transaction.
     * 
     * @access  public
     * @param   mixed     $blockId     Transaction block identifier
     * @return  void
     */
    public function commit($blockId = null) {
        $this->closeTransactionBlock($blockId);
    }
    
    /**
     * Creates a SQL 'delete' object.
     * 
     * @access public
     * @return OrmSqlDelete
     */
    public function createSqlDelete() {
        require_once 'orm/sql/OrmSqlDelete.php';
        return new OrmSqlDelete();
    }
    
    /**
     * Creates a SQL 'insert' object.
     * 
     * @access public
     * @return OrmSqlInsert
     */
    public function createSqlInsert() {
        require_once 'orm/sql/OrmSqlInsert.php';
        return new OrmSqlInsert();
    }
    
    /**
     * Creates a SQL 'select' object.
     * 
     * @access public
     * @return OrmSqlSelect
     */
    public function createSqlSelect() {
        require_once 'orm/sql/OrmSqlSelect.php';
        return new OrmSqlSelect();
    }
    
    /**
     * Creates a SQL 'update' object.
     * 
     * @access public
     * @return OrmSqlUpdate
     */
    public function createSqlUpdate() {
        require_once 'orm/sql/OrmSqlUpdate.php';
        return new OrmSqlUpdate();
    }
    
    /**
     * SQL delete method.
     * 
     * This method is provided as a stub to filter 'delete' calls to 
     * <em>runQuery</em> method.
     * 
     * @access  public
     * @param   mixed       $sql      OrmSql or sql's string
     * @param   array       $vars     Variables to use in query
     * @return  resultset
     */
    public function delete($sql, $vars = null) {
        return $this->runQuery($sql, $vars);
    }
    
    /**
     * This method calculates a primary key before insert is intended. This 
     * method is useful when you need to get the id from another table, from 
     * a sequence, or any other source.
     * If you don't want to generate the PK here, just return null.
     * You can see @docs for a complete explanation about PK creation.
     * 
     * @access  public
     * @param   Bean          $bean   Bean that is being inserted in DB
     * @param   OrmCore       $dao    OrmCore that is performing the operation
     * @see     getCreatedPK
     * @return  mixed
     */
    public function getCalculatedPK($bean, $dao) {
        return null;
    }
    
    /**
     * Returns the connection object.
     * 
     * This base class aims to use it as an object, following the preferred 
     * implementation setted at the interface.
     * 
     * @access  public
     * @return  object
     */
    function getConn() {
        return $this->conn;
    }
    
    /**
     * This method looks for the primary key created automatically by the DB
     * after insert is performed. This is useful when you need to get the id 
     * created by a trigger, auto increment column, or any other source.
     * If you don't want to look for the created PK here, just return null.
     * You can see @docs for a complete explanation about PK creation.
     * 
     * @access  public
     * @param   Bean          $bean   Bean that is being inserted in DB
     * @param   OrmCore       $dao    OrmCore that is performing the operation
     * @see     getCalculatedPK
     * @return  mixed
     */
    public function getCreatedPK($bean, $dao) {
        return null;
    }
    
    /**
     * Returns stored <em>Dsn</em> string.
     *
     * @access  public
     * @return  string
     */
    function getDsn() {
        return $this->dsn;
    }
    
    /**
     * Returns first value from query.
     * 
     * This method is really useful to get just the first column's value from 
     * first row of a query, regardless of the column's name. As with 
     * <em>runQuery</em>, you can pass a query or a OrmSqlSelect as first 
     * parameter.
     * 
     * @param   mixed       $sql      OrmSql or sql's string
     * @param   array       $vars     Variables to use in query
     * @return  mixed
     */
    public function getValue($sql) {
        $rs = $this->select($sql);
        $row = $rs->fetchRow($this->mapFetchmode(self::FETCHMODE_LIST));
        return $row[0];
    }
    
    /**
     * Returns the identifier for the transaction that is currently active.
     * A null value means no transaction in progress.
     *
     * @access  public
     * @return  mixed
     */
    public function getTransactionBlock() {
        return $this->transactionBlock;
    }
    
    /**
     * SQL insert method.
     * 
     * This method is provided as a stub to filter 'insert' calls to 
     * <em>runQuery</em> method.
     * 
     * @access  public
     * @param   mixed       $sql      OrmSql or sql's string
     * @param   array       $vars     Variables to use in query
     * @return  resultset
     */
    public function insert($sql, $vars = null) {
        return $this->runQuery($sql, $vars);
    }
    
    /**
     * This method maps fetch mode from standard ones defined in 
     * <em>OrmIDb</em> to the ones from the specific implementation.
     * 
     * @access  public
     * @param   int       $ormFetchmode      Orm's standard fetch mode
     * @return  mixed
     */
    public function mapFetchmode($ormFetchmode) {
        return $ormFetchmode;
    }
    
    /**
     * Starts a database transaction block, and returns a block identifier.
     * 
     * @access  protected
     * @return  mixed
     */
    protected function openTransactionBlock() {
        $this->transactionBlock += 1;
        return $this->getTransactionBlock();
    }
    
    /**
     * Prepares query values to be used.
     * It will return an array with values, unless you pass a second parameter 
     * with only one value, that is not null nor array, so it will just 
     * return it again.
     * 
     * @access  protected
     * @param   mixed       $sql      OrmSql or sql's string
     * @param   array       $vars     Variables to use in query
     * @return  mixed
     */
    protected function prepareVars($sql, $vars = null) {
        if (ObjectInspector::isA($sql, 'OrmSql')) {
            if (is_null($vars)) {
                $vars = $sql->getValues();
            } else {
                if (!is_array($vars)) {
                    $vars = array($vars);
                }
                $vars = array_merge($sql->getValues(), $vars);
            }
        } else {
            if (is_null($vars)) {
                $vars = array();
            }
        }
        return $vars;
    }
    
    /**
     * Rollbacks a database transaction.
     * 
     * @access  public
     * @param   mixed     $blockId     Transaction block identifier
     * @return  void
     */
    public function rollback($blockId = null) {
        $this->closeTransactionBlock($blockId);
    }
    
    /**
     * SQL select method.
     * 
     * This method is provided as a stub to filter 'select' calls to 
     * <em>runQuery</em> method.
     * 
     * @access  public
     * @param   mixed       $sql      OrmSql or sql's string
     * @param   array       $vars     Variables to use in query
     * @return  resultset
     */
    public function select($sql, $vars = null) {
        return $this->runQuery($sql, $vars);
    }
    
    /**
     * Connection setter.
     *
     * @access  public
     * @param   object      $conn       Connection
     * @see     getConn
     */
    public function setConn($conn) {
        return $this->conn = $conn;
    }
    
    /**
     * DSN setter. 
     * <strong>Important:</strong> If receives a not null DSN, it creates a 
     * new database connection. If receives a null DSN, it try to close the 
     * connection.
     *
     * @access  public
     * @param   string   $dsn        DSN info for database connection
     */
    public function setDsn($dsn) {
        $this->dsn = $dsn;
        if (!is_null($dsn)) {
            $this->connect();
        } else if ($this->getConn() != null) {
            $this->disconnect();
        }
    }
    
    /**
     * SQL update method.
     * 
     * This method is provided as a stub to filter 'update' calls to 
     * <em>runQuery</em> method.
     * 
     * @access  public
     * @param   mixed       $sql      OrmSql or sql's string
     * @param   array       $vars     Variables to use in query
     * @return  resultset
     */
    public function update($sql, $vars = null) {
        return $this->runQuery($sql, $vars);
    }
    
}

?>