PHP Classes

File: DATA/MySQL5/Row.php

Recommend this page to a friend!
  Classes of Martin Alterisio   DATA   DATA/MySQL5/Row.php   Download  
File: DATA/MySQL5/Row.php
Role: Class source
Content type: text/plain
Description: This class is the abstraction of a MySQL5 table row implementing the array access and iteration behavior.
Class: DATA
Access data stored in MySQL tables like arrays
Author: By
Last change: v0.8 - abstraction of sql data types and inboxing of these values into objects with constraints checking
Date: 17 years ago
Size: 9,268 bytes
 

Contents

Class file image Download
<?php
/**
 * @package DATA_MySQL5
 */

/**
 * This class is the abstraction of a MySQL5 table row implementing the
 * array access and iteration behavior.
 *
 * Field modification will affect the real database row, unless the object
 * has been marked as read-only (example: when using integer index access
 * to the field), or unless the object has been detached (example: cloned
 * objects). Only the field modified will be affected.
 *
 * When cloning a row the new object will become detached from database and
 * modifications won't be proxied to the database. The detached object will
 * become reattached once it is inserted again the in table array. This can
 * be used to bundle modifications to a row before commiting them, or
 * duplicating rows with small changes.
 *
 * Data won't remain updated to external changes. Preferred use of row
 * objects is as volatile containers.
 *
 * Field data returned will be inboxed into representative objects
 * ({@link DATA_SQLChar}, {@link DATA_SQLVarchar}, {@link DATA_SQLDecimal},
 * {@link DATA_SQLInt}, {@link DATA_SQLSmallInt}
 * {@link DATA_SQLDate}, {@link DATA_SQLTime}, {@link DATA_SQLDatetime}).
 *
 * Examples:
 * <code>
 * // Retrieve a field value
 * $fieldValue = $DB['table'][$rowOffset]['field'];
 *
 * // Change a field value
 * $DB['table'][$rowOffset]['field'] = '...';
 * // or
 * $row = $DB['table'][$rowOffset];
 * $row['field'] = '...';
 *
 * // Cloning
 * $row = clone $DB['table'][$rowOffset];
 * $row['field'] = '...';
 * $row['field2'] = '...';
 * $DB['table'][$rowOffset] = $row;
 * // or on a new row or offset
 * $DB['table'][] = $row;
 * </code>
 */
class DATA_MySQL5_Row implements ArrayAccess, Countable, IteratorAggregate {
   
/**
     * The table name.
     * @var string
     */
   
protected $table;
   
/**
     * The row data. It may be in raw or inboxed format.
     * @var array
     */
   
protected $data;
   
/**
     * Array for field checking purposes.
     * @var array
     */
   
protected $fields;
   
/**
     * Read only flag.
     * @var bool
     */
   
protected $readOnly;
   
/**
     * Detached from db flag
     * @var bool
     */
   
protected $detached;
   
/**
     * Disables inboxing in this object.
     * @var bool
     */
   
protected $inboxingDisabled;
   
   
/**
     * Default constructor.
     *
     * @access protected
     * @param string $table The table.
     * @param array $data The fetched row data.
     * @param bool $readOnly The created object has read only access if set to true.
     * Optional, defaults to false.
     */
   
public function __construct($table, $data, $readOnly = false) {
       
$this->table = $table;
       
$this->setData($data);
       
$this->readOnly = $readOnly;
       
$this->detached = false;
       
$this->inboxingDisabled = false;
    }
   
   
/**
     * isset(..) handler. Indicates if field exists.
     *
     * @param string $field The field name.
     * @return bool True if field exists, false otherwise.
     */
   
public function offsetExists($field) {
        if (
is_int($field)) {
            return
count($this->data) > $field && $field >= 0;
        } else {
            return isset(
$this->fields[$field]);
        }
    }
   
   
/**
     * [..] handler. Returns the value of the field requested.
     *
     * Throws {@link DATA_FieldDoesntExist}.
     *
     * @param string $field The field name.
     * @return string The field value.
     */
   
public function offsetGet($field) {
        if (!
$this->offsetExists($field)) {
            throw new
DATA_FieldDoesntExist($this->table, $field);
        }
        if (
is_int($field)) {
           
$fields = array_keys($this->data);
           
$field = $fields[$field];
        }
        return
$this->inboxField($field, $this->data[$field]);
    }
   
   
/**
     * [..] = handler. Sets the value of a field.
     *
     * Throws {@link DATA_ReadOnly}, {@link DATA_FieldDoesntExist},
     * {@link DATA_SQLTypeConstraintFailed}.
     *
     * @param string $field The field name.
     * @param string $value The field value.
     */
   
public function offsetSet($field, $value) {
        if (
$this->readOnly) {
            throw new
DATA_ReadOnly();
        }
        if (!
$this->offsetExists($field)) {
            throw new
DATA_FieldDoesntExist($this->table, $field);
        }
        if (
is_int($field)) {
           
$fields = array_keys($this->data);
           
$field = $fields[$field];
        }
        try {
           
$value = $this->inboxField($field, $value);
        } catch (
DATA_SQLTypeConstraintFailed $exception) {
           
$exception->setTable($this->table);
           
$exception->setField($field);
            throw
$exception;
        }
        if (!
$this->detached) {
           
$keys = DATA_MySQL5_Schema::getPrimaryKey($this->table);
           
DATA_MySQL5_Access::query("
                UPDATE `
{$this->table}`
                   SET `
{$field}` = " . DATA_MySQL5_Access::prepareData($value) . "
                 WHERE `
{$keys[0]}` = '" . DATA_MySQL5_Access::escape($this->data[$keys[0]]) . "'
            "
);
        }
       
$this->data[$field] = $value;
    }
   
   
/**
     * unset(..) handler. Fields cannot be unset.
     *
     * Will throw {@link DATA_CannotRemoveField DATA_CannotRemoveField exception}.
     */
   
public function offsetUnset($offset) {
        throw new
DATA_CannotRemoveField();
    }
   
   
/**
     * count(..) handler. Returns fields count.
     *
     * @return int How many fields there are on this row.
     */
   
public function count() {
        return
count($this->data);
    }
   
   
/**
     * Provides the iterator to be used on a foreach loop.
     *
     * @return ArrayIterator The fields data iterator.
     */
   
public function getIterator() {
        if (
$this->inboxingDisabled) {
            return new
ArrayIterator($this->data);
        }
       
$inboxedData = array();
        foreach (
$this->data as $field => $value) {
           
$inboxedData[$field] = DATA_MySQL5_Schema::getSQLTypeFactory($this->table, $field)->inbox($value);
        }
        return new
ArrayIterator($inboxedData);
    }
   
   
/**
     * clone handler. Detaches cloned object from the database.
     */
   
public function __clone() {
       
$this->readOnly = false;
       
$this->detached = true;
    }
   
   
/**
     * Reattaches the object to the database.
     *
     * @param int $autoid Autogenerated numeric id. Optional, 0 if no id was generated.
     */
   
public function reattach($autoid = 0) {
        if (
$autoid != 0) {
           
$autofield = DATA_MySQL5_Schema::getAutoIncrementField($this->table);
           
$this->data[$autofield] = (int)$autoid;
        }
       
$sql = "SELECT * FROM `{$this->table}` ";
       
$sql .= $this->buildWhereStatement();
       
$query = DATA_MySQL5_Access::query($sql);
       
$this->setData(DATA_MySQL5_Access::fetchAssoc($query));
       
$this->readOnly = false;
       
$this->detached = false;
    }
   
   
/**
     * Builds a where statement to select this row.
     *
     * @return string SQL where statement to select the requested row.
     */
   
protected function buildWhereStatement() {
       
$keys = DATA_MySQL5_Schema::getPrimaryKey($this->table);
       
$sql = 'WHERE';
       
$sep = '';
        foreach (
$keys as $key) {
           
$sql .= "$sep `$key` = " . DATA_MySQL5_Access::prepareData($this->data[$key]);
           
$sep = ' AND';
        }
        return
$sql;
    }
   
   
/**
     * Sets the fetched row data.
     *
     * @param array $data The fetched row data.
     */
   
protected function setData($data) {
       
$this->data = $data;
       
$this->fields = array();
        foreach (
array_keys($data) as $field) {
           
$this->fields[$field] = true;
        }
    }
   
   
/**
     * Returns inboxed version of the field provided.
     *
     * Throws {@link DATA_SQLTypeConstraintFailed}.
     *
     * @param string $field The field name.
     * @param null|int|string|DATA_SQLType $value The field value
     * @return DATA_SQLType Inboxed field.
     */
   
protected function inboxField($field, $value) {
        if (
$this->inboxingDisabled) return $value;
        return
DATA_MySQL5_Schema::getSQLTypeFactory($this->table, $field)->inbox($value);
    }
   
   
/**
     * Member property overloading.
     *
     * withoutInboxing property returns a db object with inboxing of
     * mysql types disabled.
     *
     * @param string $propname Property name.
     * @return mixed Property value.
     */
   
public function __get($propname) {
        if (
$propname == 'withoutInboxing') {
           
$newRow = clone $this;
           
$newRow->readOnly = $this->readOnly;
           
$newRow->detached = $this->detached;
           
$newRow->inboxingDisabled = true;
            return
$newRow;
        }
        throw new
Exception("Undefined property: {$propname}");
    }
}
?>