Location: PHPKode > scripts > MDB2_DataContainer > mdb2_datacontainer/MDB2_DataContainer2-0.5.3/MDB2/DataContainer2.php
<?php

/* vim: set ts=4 sw=4: */

/*
+-----------------------------------------------------------------------+
| Copyright (c) 2002-2007, Mika Tuupola                                 |
| All rights reserved.                                                  |
|                                                                       |
| Redistribution and use in source and binary forms, with or without    |
| modification, are permitted provided that the following conditions    |
| are met:                                                              |
|                                                                       |
| o Redistributions of source code must retain the above copyright      |
|   notice, this list of conditions and the following disclaimer.       |
| o 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.|
| o 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.  |
|                                                                       |
+-----------------------------------------------------------------------+
| Authors: Mika Tuupola <hide@address.com>                        |
|          Toni Viemerö <hide@address.com>                           |
+-----------------------------------------------------------------------+
*/

/* $Id: DataContainer2.php 161 2008-04-08 19:32:00Z tuupola $ */

require_once 'MDB2.php';
require_once 'PEAR.php';
require_once 'MDB2/DataContainer2/Overload.php';
require_once 'MDB2/DataContainer2/Inflector.php';

/**
  * MDB2_DataContainer class
  *
  * @version    $Revision$
  * @author     Mika Tuupola <hide@address.com>
  * @author     Toni Viemerö <hide@address.com>
  */

class MDB2_DataContainer2 extends MDB2_DataContainer2_Overload {

  /**
    * Id of the data if exists the database.
    *
    * Should only have value only if the data was read from the
    * database or if you are updating existing object.
    *
    * @access   private
    */
    var $id;

  /**
    * PEAR database handler
    * @accesss  private
    * @see      MDB2
    */
    var $dbh;

  /**
    * Table in which data is stored
    * @access   private
    */
    var $table;

  /**
    * What column to use as non-default key
    * @access   private
    */
    var $key;

  /**
    * The class constructor
    *
    * If $params is not an array but instead numeric, it will be
    * considered as a shortcut to be $params[id]
    *
    * If $params[id] is given data can be load():ed from database.
    *
    * If $params[key] is given that column will be used
    * instead of the default column named 'id' when load():ing data
    * from database.
    *
    * If $params[key] was given you also must provide $params[$params[key]].
    * For example if $params[key] = 'foo', you must give something
    * like $params[foo] = 27.
    *
    * $params[table] contains the name of the table in the database.
    * This parameter is mandatory.
    *
    * Rest of the data given in $params will be mapped against
    * object properties.
    *
    * @param    object  $dbh a PEAR database handler object.
    * @param    mixed   $params numerical id or array of parameters
    * @return   object
    */

    function __construct($dbh, $params = array()) {

        $class = get_class($this);

        /* default tablename taken from classname */
        $table = isset($params['table']) ? $params['table'] :  
                 MDB2_DataContainer2_Inflector::tableize($class);
        $this->setTable($table);

        /* if only passing id no need to pass it in array */
        if (is_array($params)) {
            $this->setProperties($params);
        } else if (is_numeric($params)){
            $this->setId($params);
        }
        $this->setDbh($dbh);
    }

  /**
    * Load the container
    *
    * Populates the container object with data from database.
    * This is possible only if container has an id or an alternate
    * key and its value is available.
    *
    * @return   mixed true on success PEAR error on failure
    */

    function load() {

        $result = true;

        /* if we have an id or key load up data from the database  */
        /* and discard any possible data given in $params.         */
        /* id overrides anything                                   */
        if ($this->id) {
            $this->key = 'id';
        }
        if ($this->key) {

            /* do not do a SELECT * but SELECT x,y,z ... instead */
            $var    = get_object_vars($this);

            /* This wont work prior to PHP 4.2.0 */
            $ignore = get_class_vars('MDB2_DataContainer2');
            foreach($ignore as $key => $val) {
                unset($var[$key]);
            }

            $select  = implode(',', array_keys($var));
            $select .= ',id';

            $query   = "SELECT $select FROM $this->table
                        WHERE ($this->key='{$this->{$this->key}}') ";

            /* it would make sense to use getRow() performancewise */
            /* but we could not warn if there was multiple matches */
            $result = $this->dbh->queryAll($query, array(), MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($result)) {

            } else {

                $count = count($result);
                /* SELECT did not return anything */
                if ($count == 0) {
                    $message = sprintf('No data found for key %s with value %s.',
                                        $this->key,
                                        $this->{$this->key});

                    $result = PEAR::raiseError($message);
                } elseif ($count == 1) {
                    foreach ($result[0] as $key=>$val) {
                        /* TODO: ugly hack to fix mixed case trouble */
                        $key = strtolower($key);
                        $this->$key = $val;
                    }
                    /* successfull load returns true */
                    $result = true;
                } elseif ($count > 1) {
                    /* use first match and discard others additionally */
                    /* raise an error                                 */
                    foreach ($result[0] as $key=>$val) {
                        /* TODO: ugly hack to fix mixed case trouble */
                        $key = strtolower($key);
                        $this->$key = $val;
                    }
                    $message = sprintf('%d matches found. Using first match.',
                                        $count);

                    $result = PEAR::raiseError($message);
                }

            }
        } else {
            $result = PEAR::raiseError('Container does not have a key.');
        }
        return($result);
    }

  /**
    * Save the container
    *
    * If $id is not given will use the objects own id. If $id not
    * given and object does not have an $id will set the $id when
    * executing the INSERT.
    *
    * @param    integer $id (optional)
    * @return   mixed true on success PEAR error on failure
    */

    function save($id='') {

        /* We need to load MDB2 Extended module for autoExecute() to work */
        $this->dbh->loadModule('Extended');

        /* magic, if $id was given as a parameter, set my id to it and  */
        /* because $id is set we will do an UPDATE. TODO: This will     */
        /* still fail if $id does not exist.                            */

        if ($id) {
             $this->id = $id;

        /* if $id was not given try to use objects own id. If even that */
        /* does not exist the object must have been created from        */
        /* submitted data and the $id will be empty -> we must do       */
        /* an INSERT.                                                   */

        } else {
            $id       = $this->id;
        }

        if ($id) {
            $mode  = MDB2_AUTOQUERY_UPDATE;
            $where = 'id=' . $this->getId();
        } else {
            $mode  = MDB2_AUTOQUERY_INSERT;
            $where = false;
            $this->id  = $this->dbh->nextID($this->table);
        }

        $var    = get_object_vars($this);

        /* This wont work prior to PHP 4.2.0 */
        $ignore = get_class_vars('MDB2_DataContainer2');
        foreach($ignore as $key => $val) {
            unset($var[$key]);
        }
        /* bring back id when needed */
        if ($mode == MDB2_AUTOQUERY_INSERT) {
            $var['id'] = $this->getId();
        }

        $table = $this->getTable();

        $result = $this->dbh->extended->autoExecute($table, $var, $mode, $where);

        return($result);
    }

  /**
    * Delete the object
    *
    * @return   mixed true on success PEAR error on failure
    */

    function delete() {
        if ($this->id) {
            $query = "DELETE FROM $this->table WHERE id='$this->id' ";
            $retval = $this->dbh->query($query);
        } else {
            $retval = PEAR::raiseError('Object does not have an id.');
        }
        return($retval);
    }


   /**
    * Set class properties
    *
    * @access public
    * @param  array  $params
    */

    function setProperties($params) {

        /* TODO: PEAR errorhandling if $params != array    */
        /* TODO: add possibility to pass data as an object */
        if (is_array($params)) {
            foreach ($params as $key => $value) {
                $method = 'set' . $key;
                $this->$method($value);
            }
        }
    }

  /**
    * Get array of DataContainer
    *
    * $params[classname] has the name of the class(es) function
    * has to return.
    *
    * $params[classname] has the name of the table to load data
    * from. If not given defaults to $params[classname].
    *
    * $params[order] translates to 'ORDER BY $params[order]'
    *
    * $params[where] translates to 'WHERE $params[where]'
    *
    * $params[limit] can be given one or two values. If only one
    * value is given it will be considered as $count. If two values
    * are given they will be considered as $from, $count. Value can
    * be given as a string or an array. For example: '5,10' or
    * array(5,10). If only one value is given $from will be
    * considered as 0.
    *
    * @param    object  $dbh a PEAR database handler object.
    * @param    array   $params (table, classname, where, order, limit, query)
    * @access   static
    * @return   mixed   array of objects on success PEAR_Error on failure
    */

    /* TODO: Classname still needs to be given as parameter       */
    /*       until there is a way for static method to determine  */
    /*       which class it belongs to. Make this more elegant.   */

    public static function find($dbh, $params='', $caller=__CLASS__) {
        /* TODO: hack while we are waiting for PHP 5.3, expect extending  */
        /* class to have static function like the following:              */
        /* static function find($dbh, $params='', $caller=__CLASS__) {    */
        /*    parent::find($dbh, $params, $caller);                       */
        /* }                                                              */
        $class  = isset($params['classname']) ? $params['classname'] : $caller;

        $retval = array();

        if (!(trim($class))) {
            $retval = PEAR::raiseError('Need $params[classname]');
        } elseif (!(isset($params['table']))) {
            /* defaults to $class in plural */
            $params['table'] = MDB2_DataContainer2_Inflector::tableize($class);
        }

        if (!(PEAR::isError($retval))) {

            /* if we have an hardcoded query no need to generate one  */
            if (isset($params['query'])) {

                $result = $dbh->query($params['query']);

            } else {

                $r      = new ReflectionClass($class);
                $var    = $r->getdefaultProperties();

                $r      = new ReflectionClass('MDB2_DataContainer2');
                $ignore = $r->getdefaultProperties();

                foreach($ignore as $key => $val) {
                    unset($var[$key]);
                }
                
                $select  = implode(',', array_keys($var));
                $select .= ',id';

                $query = "SELECT $select FROM $params[table] ";
                if (isset($params['where'])) {
                    $query .= "WHERE $params[where] ";
                }
                if (isset($params['orderby'])) {
                    $query .= "ORDER BY $params[orderby] ";
                } elseif (isset($params['order'])) {
                    $query .= "ORDER BY $params[order] ";
                }
                /* TODO: test with other drivers than pgsql, mysql and mssql  */
                if (isset($params['limit'])) {
                    if (is_array($params['limit'])) {
                        $from  = $params['limit'][0];
                        $count = $params['limit'][1];
                    } else {
                        /* split by whitespace and/or comma */
                        $temp = preg_split ('/[\s,]+/', $params['limit'], 2 );
                        if (count($temp) == 2) {
                          $from  = $temp[0];
                          $count = $temp[1];
                        } else {
                          $from  = 0;
                          $count = $temp[0];
                        }
                    }
                    /* We need to load MDB2 Extended module for limitQuery() to work */
                    $dbh->loadModule('Extended');

                    $result = $dbh->extended->limitQuery($query, array(), $count, $from);
                } else {
                    $result = $dbh->query($query);
                }
            }
            if (MDB2::isError($result)) {
                $retval = $result;
            } else {
                $retval = array();

                /* no need to call load() after fetching objects  */
                /* uses more memory but causes only one SELECT    */
                while ($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
                    $row['table']  = $params['table'];
                    $c = new $class($dbh, $row);
                    array_push($retval, $c);
                    unset($c);
                }
            }
        }
        return($retval);
    }

}
Return current item: MDB2_DataContainer