Login   Register  
PHP Classes
elePHPant
Icontem

File: DATA/MySQL5/Row.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  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: 7 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 ArrayAccessCountableIteratorAggregate {
    
/**
     * 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}");
    }
}
?>