Skip to content

Instantly share code, notes, and snippets.

@bubach
Created July 5, 2014 17:55
Show Gist options
  • Save bubach/fa0212c52a52142e7098 to your computer and use it in GitHub Desktop.
Save bubach/fa0212c52a52142e7098 to your computer and use it in GitHub Desktop.
<?php
/*
*
* CBL ActiveRecord - an O/R mapping library for PHP5
* Copyright (C) 2005 Cybozu Labs, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* For more information, see:
* http://31tools.com/cbl_activerecord/
*/
/**
* exception class for CBL_ActiveRecord
*/
class CBL_ActiveRecordException extends Exception
{
function __construct( $message, $code )
{
return parent::__construct( $message, $code );
}
}
// exception code
define( 'ar_construct_by_id', 0 );
define( 'ar_construct_by_constraints', 1 );
//if( ! is_numeric( @PDO_FETCH_ASSOC ) )
//{
define( 'PDO_FETCH_ASSOC', PDO::FETCH_ASSOC );
define( 'PDO_PARAM_INT', PDO::PARAM_INT );
define( 'PDO_PARAM_STR', PDO::PARAM_STR );
//}
/**
* active record
* @author HATA,Shinya
*/
class CBL_ActiveRecord
{
protected static $default_pdo = null;
/**
* set default PDO instnace
* @param object pdo PDO instance
*/
static function setDefaultPDO( $pdo )
{
self::$default_pdo = $pdo;
}
/**
* get default PDO instance
* @return object PDO instance
*/
static function &getDefaultPDO()
{
return self::$default_pdo;
}
protected $pdo = null;
protected $table = null;
protected $row = array();
protected $constraints = array();
protected $children = array();
/**
* constructor
* @param mixed param PDO instance,
* or record id (if integer),
* or constraints (if array) but param['pdo'] is PDO instance
*/
function __construct( $param = null )
{
$this->pdo = self::$default_pdo;
$class_name = strtolower( get_class($this) );
if( $class_name != 'cbl_activerecord' )
{
$this->table = $class_name;
}
if( ! $param ) return;
if( strtolower( get_class( $param ) ) == 'pdo' )
{
// parameter is PDO instance
$this->pdo = $param;
}
else if( is_numeric( $param ) || is_string( $param ) )
{
// parameter is numeric, then treat as 'id'
if( ! $this->_find( $param ) )
{
throw new CBL_ActiveRecordException(
'Fail to construct by record id.',
ar_construct_by_id );
}
}
else if( is_array( $param ) )
{
// parameter is array
if( @$param['pdo'] &&
strtolower( get_class( $param['pdo'] ) ) == 'pdo' )
{
$this->pdo = $param['pdo'];
unset( $param['pdo'] );
}
if( count( $param ) )
{
$this->constraints = $param;
if( ! $this->_find() )
{
throw new CBL_ActiveRecordException(
'Fail to construct by constraints.',
ar_construct_by_constraints );
}
}
}
}
/**
* get PDO instance
*/
function getPDO()
{
return $this->pdo;
}
/**
* get property
* @param string name property name
* @return mixed property value
*/
public function __get( $name )
{
assert( is_array( $this->row ) );
if( array_key_exists( $name, $this->row ) )
{
// record column value
return $this->row[$name];
}
// get child object
//$id = intval( $this->row['id'] );
$id = @$this->row['id']; // modify at 2005/12/14 for string id
if( $id )
{
$foreign_key = $this->table . '_id';
assert( is_array( $this->children ) );
if( array_key_exists( $name, $this->children ) &&
$this->children[$name]->getConstraint( $foreign_key ) == $id )
{
// return cashed instance
return $this->children[$name];
}
$class_name = ucfirst( $name );
if( class_exists( $class_name, FALSE ) )
{
// create instance
$child = new $class_name( $this->pdo );
$child->setConstraint( $foreign_key, $id );
$this->children[$name] = $child;
return $child;
}
}
return null;
}
/**
* set property
* @param string name property name
* @param mixed value property value
*/
public function __set( $name, $value )
{
if( ! is_array( $this->row ) )
{
$this->row = array();
}
$this->row[$name] = $value;
}
/**
* set constraints
* @param string name column name
* @param mixed value column value
*/
function setConstraint( $name, $value )
{
$this->constraints[$name] = $value;
}
/**
* get constraints
* @param string name column name
* @return mixed column value
*/
function getConstraint( $name )
{
return array_key_exists( $name, $this->constraints) ? $this->constraints[$name] : null;
}
/**
* get one record
* @param mixed id record id
* @return CBL_ActiveRecord this instance, or null if failed
*/
function find( $id = null, $params = null )
{
$record = clone( $this );
return $record->_find( $id, $params ) ? $record : null;
}
/**
* get one record helper
* @param mixed id record id
* @return boolean
*/
protected function _find( $id = null, $params = null )
{
if( is_null( $params ) )
{
$params = array();
}
if( $id )
{
if( is_numeric( $id ) && ! isset( $this->has_string_id ) )
{
$id = intval( $id );
}
$this->constraints['id'] = $id;
}
if( @$params['columns'] )
{
$sql = "SELECT {$params['columns']} FROM `{$this->table}`";
}
else
{
$sql = "SELECT * FROM `{$this->table}`";
}
if( count( $this->constraints ) )
{
$sql .= ' WHERE ' . $this->_makeANDConstraints( $this->constraints );
$binding_params = $this->_makeBindingParams( $this->constraints );
}
$sql .= ';';
$sth = $this->pdo->prepare( $sql );
if( ! $sth )
{
$err = $this->pdo->errorInfo();
trigger_error( "{$err[2]}:{$sql}", E_USER_ERROR );
}
if( ! $sth->execute( @$binding_params ) )
{
$err = $sth->errorInfo();
trigger_error( "{$err[2]}:{$sql}", E_USER_ERROR );
}
$row = $sth->fetch( PDO_FETCH_ASSOC );
$sth->closeCursor();
if( ! $row ) return FALSE;
$this->row = $row;
return TRUE;
}
/**
* get record list
* @param array params option array
* params['conditions'] : WHERE phrase in SQL
* params['order'] : ORDER phrase in SQL
* @return array array of CBL_ActiveRecord
*/
function find_all( $params = null, $join = null )
{
if (! is_array($params)) $params = array();
if (! is_array($join)) $join = array();
if( @$params['columns'] )
{
$sql = "SELECT {$params['columns']} FROM `{$this->table}`";
}
else
{
$sql = "SELECT * FROM `{$this->table}`";
}
if (count($join) && @$join['table'] && @$join['lhs'] && @$join['rhs']) {
$sql .= " INNER JOIN `{$join['table']}`".
" ON `{$this->table}`.{$join['lhs']}=`{$join['table']}`.{$join['rhs']}";
}
$where = FALSE;
if( count( $this->constraints ) )
{
$sql .= ' WHERE ' . $this->_makeANDConstraints( $this->constraints );
$where = TRUE;
}
if( @$params['conditions'] )
{
if( $where )
{
$sql .= " AND ({$params['conditions']})";
}
else
{
$sql .= " WHERE {$params['conditions']}";
$where = TRUE;
}
}
if( @$params['order'] )
{
$sql .= " ORDER BY {$params['order']}";
}
if( array_key_exists( 'limit', $params ) )
{
$limit = intval( $params['limit'] );
if( array_key_exists( 'offset', $params ) )
{
$offset = intval( $params['offset'] );
$sql .= " LIMIT {$limit} OFFSET {$offset}";
}
else
{
$sql .= " LIMIT {$limit}";
}
}
$sql .= ';';
$binding_params = $this->_makeBindingParams( $this->constraints );
$sth = $this->pdo->prepare( $sql );
if( ! $sth )
{
$err = $this->pdo->errorInfo();
trigger_error( "{$err[2]}:{$sql}", E_USER_ERROR );
}
if( ! $sth->execute( $binding_params ) )
{
$err = $sth->errorInfo();
trigger_error( "{$err[2]}:{$sql}", E_USER_ERROR );
}
$row_list = $sth->fetchAll( PDO_FETCH_ASSOC );
$item_list = array();
foreach( $row_list as $row )
{
$item = new $this->table( $this->pdo );
$item->row = $row;
$item->constraints = $this->constraints;
if (isset($row['id'])) {
$item_list[$row['id']] = $item;
} else {
$item_list[] = $item;
}
}
return $item_list;
}
/**
* insert record to table, or update record data
*/
function save()
{
if( @$this->row['id'] )
{
$this->update();
}
else
{
unset( $this->row['id'] );
$this->insert();
}
}
/**
* delete record from table
* @param mixed id record id
* @return boolean
*/
function delete( $id )
{
if( is_numeric( $id ) && ! isset( $this->has_string_id ) )
{
$id = intval( $id );
}
$this->constraints['id'] = $id;
$sql = "DELETE FROM `{$this->table}` WHERE " .
$this->_makeANDConstraints( $this->constraints ) . ';';
$binding_params = $this->_makeBindingParams( $this->constraints );
$sth = $this->pdo->prepare( $sql );
if( ! $sth )
{
$err = $this->pdo->errorInfo();
trigger_error( "{$err[2]}:{$sql}", E_USER_ERROR );
}
if( ! $sth->execute( $binding_params ) )
{
$err = $sth->errorInfo();
trigger_error( "{$err[2]}:{$sql}", E_USER_ERROR );
}
$this->row = array();
return $sth->rowCount() > 0;
}
function count($params = null)
{
$sql = "SELECT COUNT(*) FROM `{$this->table}`";
if (isset($params['conditions']) && $params['conditions'] != '')
{
$sql .= " WHERE {$params['conditions']}";
}
$sql .= ';';
$sth = $this->pdo->query( $sql );
return intval( $sth->fetchColumn() );
}
/*
function _update()
{
$values = $this->row;
unset( $values['id'] );
if( ! count( $values ) )
{
trigger_error( 'No record value.', E_USER_ERROR );
}
$sql = "UPDATE `{$this->table}` SET " .
$this->_makeUPDATEValues( $values ) .
" WHERE `{$this->table}`.id=:id;";
$binding_params = $this->_makeBindingParams( $this->row );
$sth = $this->pdo->prepare( $sql );
if( ! $sth )
{
$err = $this->pdo->errorInfo();
trigger_error( "{$err[2]}:{$sql}", E_USER_ERROR );
}
if( ! $sth->execute( $binding_params ) )
{
$err = $sth->errorInfo();
trigger_error( "{$err[2]}:{$sql}", E_USER_ERROR );
}
return $sth->rowCount() > 0;
}
*/
function update( $id_list = array() )
{
$values = $this->row;
unset($values['id']);
if (! count( $values)) {
trigger_error( 'No record value.', E_USER_ERROR );
}
$sql = "UPDATE `{$this->table}` SET ".
$this->_makeUPDATEValues($values);
if (isset($this->row['id']) && $this->row['id']) {
$sql .= " WHERE `{$this->table}`.id=:id;";
} else if (count($id_list)) {
$sql .= ' WHERE '.$this->_makeIDList($id_list).';';
} else {
trigger_error('ID is not specified.', E_USER_ERROR);
}
$binding_params = $this->_makeBindingParams($this->row);
$sth = $this->pdo->prepare($sql);
if (! $sth) {
$err = $this->pdo->errorInfo();
trigger_error("{$err[2]}:{$sql}", E_USER_ERROR);
}
if (! $sth->execute($binding_params)) {
$err = $sth->errorInfo();
trigger_error("{$err[2]}:{$sql}", E_USER_ERROR);
}
return $sth->rowCount();
}
function insert()
{
$this->row = array_merge( $this->constraints, $this->row );
$binding_params = $this->_makeBindingParams( $this->row );
$sql = "INSERT INTO `{$this->table}` (" .
implode( ', ', array_keys($this->row) ) . ') VALUES(' .
implode( ', ', array_keys($binding_params) ) . ');';
$sth = $this->pdo->prepare( $sql );
if( ! $sth )
{
$err = $this->pdo->errorInfo();
trigger_error( "{$err[2]}:{$sql}", E_USER_ERROR );
}
if( ! $sth->execute( $binding_params ) )
{
$err = $sth->errorInfo();
trigger_error( "{$err[2]}:{$sql}", E_USER_ERROR );
}
if( ! @$this->row['id'] && ! isset( $this->has_string_id ) )
{
$this->row['id'] = $this->pdo->lastInsertId();
return intval( $this->row['id'] );
}
return @$this->row['id'];
}
function uniqid($len = 8, $set = TRUE)
{
if ($len < 8) {
trigger_error('ID length is short.', E_USER_ERROR);
}
$sql = "SELECT id FROM `{$this->table}` WHERE id=:id;";
$sth = $this->pdo->prepare($sql);
do {
$id = substr(md5(uniqid()), 0, $len);
$sth->execute(array('id'=>$id));
$row = $sth->fetch();
$sth->closeCursor();
} while ($row);
if ($set) {
$this->id = $id;
}
return $id;
}
function _makeANDConstraints( $array )
{
foreach( $array as $key=>$value )
{
if( is_null( $value ) )
{
$expressions[] = "`{$this->table}`.{$key} IS NULL";
}
else
{
$expressions[] = "`{$this->table}`.{$key}=:{$key}";
}
}
return implode( ' AND ', $expressions );
}
function _makeUPDATEValues( $array )
{
foreach( $array as $key=>$value )
{
$expressions[] ="{$key}=:{$key}";
}
return implode( ', ', $expressions );
}
function _makeBindingParams( $array )
{
$params = array();
foreach( $array as $key=>$value )
{
$params[":{$key}"] = $value;
}
return $params;
}
function _makeIDList( $array )
{
$expressions = array();
foreach ($array as $id) {
$expressions[] = "`{$this->table}`.id=".
$this->pdo->quote($id, isset($this->has_string_id) ? PDO_PARAM_INT : PDO_PARAM_STR);
}
return '('.implode(' OR ', $expressions).')';
}
}
class CBL_ActiveRoot extends CBL_ActiveRecord
{
public function __get( $name )
{
assert( is_array( $this->children ) );
if( array_key_exists( $name, $this->children ) )
{
return $this->children[$name];
}
$class_name = ucfirst( $name );
if( class_exists( $class_name, FALSE ) )
{
// create instance
$child = new $class_name( $this->pdo );
$this->children[$name] = $child;
return $child;
}
return null;
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment