<?php
// $Horde: turba/lib/Driver/sql.php,v 1.18.2.8 2003/02/10 01:18:10 jan Exp $

/**
 * Turba directory driver implementation for PHP's PEAR database abstraction
 * layer.
 *
 * @author  Jon Parise <jon@csh.rit.edu>
 * @version $Revision: 1.18.2.8 $
 * @since   Turba 0.0.1
 * @package turba
 */
class Turba_Driver_sql extends Turba_Driver {

    /** Handle for the current database connection. */
    var $db = null;

    /** String containg the name of the table in which the entries are
        stored. */
    var $table = '';


    /**
     * Constructs a new Turba SQL driver object.
     *
     * @param $params       Hash containing additional configuration parameters.
     */
    function Turba_Driver_sql($params)
    {
        $this->type = 'sql';
        $this->params = $params;

        include_once 'DB.php';
        $this->db = &DB::connect($params, true);
        if (!DB::isError($this->db)) {
            $this->db->setOption('optimize', 'portability');
        } else {
            $this->errno = -1;
            $this->errstr = $this->db->getMessage();
        }

        $this->table = $params['table'];
    }


    /**
     * Searches the SQL database with the given criteria and returns a
     * filtered list of results. If the criteria parameter is an empty
     * array, all records will be returned.
     *
     * @param $criteria      Array containing the search criteria.
     * @param $fields        List of fields to return.
     * @param $strict_fields (optional) Array of fields which must be matched
     *                       exactly.
     * @param const $match   Do an 'and' or an 'or' search (defaults to TURBA_SEARCH_AND).
     *
     * @return               Hash containing the search results.
     */
    function search($criteria, $fields, $strict_fields = array(), $match = TURBA_SEARCH_AND)
    {
        if ($match === TURBA_SEARCH_AND) {
            $glue = ' AND ';
        } else {
            $glue = ' OR ';
        }

        $where = '';
        if (count($criteria) > 0) {
            /* Sort into strict and non-strict fields. */
            $strict = array();
            $loose = array();
            $strict_where = '';
            $loose_where = '';
            foreach ($criteria as $key => $val) {
                if (in_array($key, $strict_fields)) {
                    $strict[$key] = $val;
                } else {
                    $loose[$key] = $val;
                }
            }

            /* Add strict sub-query. */
            if (count($strict) > 0) {
                $strict_where = ' (';
                foreach ($strict as $key => $val) {
                    if ($strict_where != ' (') {
                        $strict_where .= ' AND ';
                    }
                    $strict_where .= $key . ' = ' . $this->db->quote($val);
                }
                $strict_where .= ')';
            }

            /* Add loose sub-query. */
            if (count($loose) > 0) {
                $loose_where = ' (';
                foreach ($loose as $key => $val) {
                    if ($loose_where != ' (') {
                        $loose_where .= ' ' . $glue;
                    }
                    $loose_where .= 'LOWER(' . $key . ') LIKE LOWER(' . $this->db->quote('%' . $val . '%') . ')';
                }
                $loose_where .= ')';
            }

            if (!empty($strict_where) && !empty($loose_where)) {
                $where = ' WHERE ' . $strict_where . ' AND ' . $loose_where;
            } elseif (!empty($strict_where) || !empty($loose_where)) {
                $where = ' WHERE ' . ($strict_where ? $strict_where : $loose_where);
            }
        }

        $query  = 'SELECT ' . implode(', ', $fields) . ' ';
        $query .= 'FROM ' . $this->table;
        $query .= $where;

        /* Log the query at a DEBUG log level. */
        Horde::logMessage(sprintf('SQL search by %s: table = %s; query = "%s"',
                                  Auth::getAuth(), $this->table, $query),
                          __FILE__, __LINE__, LOG_DEBUG);

        /* Run query. */
        $result = $this->db->query($query);
        $results = array();
        if (!DB::isError($result)) {
            while ($row = $result->fetchRow()) {
                if (DB::isError($row)) {
                    return $row;
                }

                $entry = array();
                for ($i = 0; $i < count($fields); $i++) {
                    $field = $fields[$i];
                    $entry[$field] = $row[$i];
                }

                $results[] = $entry;
            }
        }

        return $results;
    }

    /**
     * Read the given data from the SQL database and returns
     * the result's fields.
     *
     * @param $criteria      Search criteria.
     * @param $id            Data identifier.
     * @param $fields        List of fields to return.
     *
     * @return               Hash containing the search results.
     */
    function read($criteria, $id, $fields)
    {
        $where = $criteria . ' = ' . $this->db->quote($id) ;

        $query  = 'SELECT ' . implode(', ', $fields) . ' ';
        $query .= 'FROM ' . $this->table . ' WHERE ' . $where;

        $result = $this->db->query($query);

        $results = array();
        if (!DB::isError($result)) {
            $row = $result->fetchRow();
            if (isset($row) && !DB::isError($row)) {
                $entry = array();

                for ($i = 0; $i < count($fields); $i++) {
                    $field = $fields[$i];
                    $entry[$field] = $row[$i];
                }

                $results[] = $entry;
            }
        }

        return $results;
    }

    /**
     * Closes the current database connection.
     *
     * @return          The result of the disconnect() call.
     */
    function close()
    {
        return $this->db->disconnect();
    }

    /**
     * Adds the specified object to the SQL database.
     */
    function addObject($attributes)
    {
        $fields = array();
        $values = array();
        foreach ($attributes as $field => $value) {
            $fields[] = $field;
            $values[] = $this->db->quote($value);
        }

        $query  = 'INSERT INTO ' . $this->table . ' (' . implode(', ', $fields) . ')';
        $query .= ' VALUES (' . implode(', ', $values) . ')';

        $result = $this->db->query($query);
        return DB::isError($result) ? $result : true;
    }

    /**
     * Deletes the specified object from the SQL database.
     */
    function removeObject($object_key, $object_id)
    {
        $where = $object_key . ' = ' . $this->db->quote($object_id);
        $query = 'DELETE FROM ' . $this->table . ' WHERE ' . $where;

        $result = $this->db->query($query);

        return (!DB::isError($result));
    }

    /**
     * Saves the specified object in the SQL database.
     */
    function setObject($object_key, $object_id, $attributes)
    {
        $where = $object_key . ' = ' . $this->db->quote($object_id);
        unset($attributes[$object_key]);

        $set = array();
        foreach ($attributes as $field => $value) {
            $set[] = $field . ' = ' . $this->db->quote($value);
        }

        $query  = 'UPDATE ' . $this->table . ' SET ' . implode(', ', $set) . ' ';
        $query .= 'WHERE ' . $where;

        $result = $this->db->query($query);

        return (!DB::isError($result));
    }

    /**
     * Create an object key for a new object.
     *
     * @param array $attributes  The attributes (in driver keys) of the
     *                           object being added.
     *
     * @return string  A unique ID for the new object.
     */
    function makeKey($attributes)
    {
        return md5(uniqid(rand()));
    }

}
?>
