You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
759 lines
19 KiB
759 lines
19 KiB
<?php
|
|
|
|
/*
|
|
|
|
Copyright (c) 2009-2019 F3::Factory/Bong Cosca, All rights reserved.
|
|
|
|
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
|
|
|
This is free software: you can redistribute it and/or modify it under the
|
|
terms of the GNU General Public License as published by the Free Software
|
|
Foundation, either version 3 of the License, or later.
|
|
|
|
Fat-Free Framework 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
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
namespace DB\SQL;
|
|
|
|
//! SQL data mapper
|
|
class Mapper extends \DB\Cursor {
|
|
|
|
//@{ Error messages
|
|
const
|
|
E_PKey='Table %s does not have a primary key';
|
|
//@}
|
|
|
|
protected
|
|
//! PDO wrapper
|
|
$db,
|
|
//! Database engine
|
|
$engine,
|
|
//! SQL table
|
|
$source,
|
|
//! SQL table (quoted)
|
|
$table,
|
|
//! Alias for SQL table
|
|
$as,
|
|
//! Last insert ID
|
|
$_id,
|
|
//! Defined fields
|
|
$fields,
|
|
//! Adhoc fields
|
|
$adhoc=[],
|
|
//! Dynamic properties
|
|
$props=[];
|
|
|
|
/**
|
|
* Return database type
|
|
* @return string
|
|
**/
|
|
function dbtype() {
|
|
return 'SQL';
|
|
}
|
|
|
|
/**
|
|
* Return mapped table
|
|
* @return string
|
|
**/
|
|
function table() {
|
|
return $this->source;
|
|
}
|
|
|
|
/**
|
|
* Return TRUE if any/specified field value has changed
|
|
* @return bool
|
|
* @param $key string
|
|
**/
|
|
function changed($key=NULL) {
|
|
if (isset($key))
|
|
return $this->fields[$key]['changed'];
|
|
foreach($this->fields as $key=>$field)
|
|
if ($field['changed'])
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Return TRUE if field is defined
|
|
* @return bool
|
|
* @param $key string
|
|
**/
|
|
function exists($key) {
|
|
return array_key_exists($key,$this->fields+$this->adhoc);
|
|
}
|
|
|
|
/**
|
|
* Assign value to field
|
|
* @return scalar
|
|
* @param $key string
|
|
* @param $val scalar
|
|
**/
|
|
function set($key,$val) {
|
|
if (array_key_exists($key,$this->fields)) {
|
|
$val=is_null($val) && $this->fields[$key]['nullable']?
|
|
NULL:$this->db->value($this->fields[$key]['pdo_type'],$val);
|
|
if ($this->fields[$key]['initial']!==$val ||
|
|
$this->fields[$key]['default']!==$val && is_null($val))
|
|
$this->fields[$key]['changed']=TRUE;
|
|
return $this->fields[$key]['value']=$val;
|
|
}
|
|
// Adjust result on existing expressions
|
|
if (isset($this->adhoc[$key]))
|
|
$this->adhoc[$key]['value']=$val;
|
|
elseif (is_string($val))
|
|
// Parenthesize expression in case it's a subquery
|
|
$this->adhoc[$key]=['expr'=>'('.$val.')','value'=>NULL];
|
|
else
|
|
$this->props[$key]=$val;
|
|
return $val;
|
|
}
|
|
|
|
/**
|
|
* Retrieve value of field
|
|
* @return scalar
|
|
* @param $key string
|
|
**/
|
|
function &get($key) {
|
|
if ($key=='_id')
|
|
return $this->_id;
|
|
elseif (array_key_exists($key,$this->fields))
|
|
return $this->fields[$key]['value'];
|
|
elseif (array_key_exists($key,$this->adhoc))
|
|
return $this->adhoc[$key]['value'];
|
|
elseif (array_key_exists($key,$this->props))
|
|
return $this->props[$key];
|
|
user_error(sprintf(self::E_Field,$key),E_USER_ERROR);
|
|
}
|
|
|
|
/**
|
|
* Clear value of field
|
|
* @return NULL
|
|
* @param $key string
|
|
**/
|
|
function clear($key) {
|
|
if (array_key_exists($key,$this->adhoc))
|
|
unset($this->adhoc[$key]);
|
|
else
|
|
unset($this->props[$key]);
|
|
}
|
|
|
|
/**
|
|
* Invoke dynamic method
|
|
* @return mixed
|
|
* @param $func string
|
|
* @param $args array
|
|
**/
|
|
function __call($func,$args) {
|
|
return call_user_func_array(
|
|
(array_key_exists($func,$this->props)?
|
|
$this->props[$func]:
|
|
$this->$func),$args
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Convert array to mapper object
|
|
* @return static
|
|
* @param $row array
|
|
**/
|
|
function factory($row) {
|
|
$mapper=clone($this);
|
|
$mapper->reset();
|
|
foreach ($row as $key=>$val) {
|
|
if (array_key_exists($key,$this->fields))
|
|
$var='fields';
|
|
elseif (array_key_exists($key,$this->adhoc))
|
|
$var='adhoc';
|
|
else
|
|
continue;
|
|
$mapper->{$var}[$key]['value']=$val;
|
|
$mapper->{$var}[$key]['initial']=$val;
|
|
if ($var=='fields' && $mapper->{$var}[$key]['pkey'])
|
|
$mapper->{$var}[$key]['previous']=$val;
|
|
}
|
|
$mapper->query=[clone($mapper)];
|
|
if (isset($mapper->trigger['load']))
|
|
\Base::instance()->call($mapper->trigger['load'],$mapper);
|
|
return $mapper;
|
|
}
|
|
|
|
/**
|
|
* Return fields of mapper object as an associative array
|
|
* @return array
|
|
* @param $obj object
|
|
**/
|
|
function cast($obj=NULL) {
|
|
if (!$obj)
|
|
$obj=$this;
|
|
return array_map(
|
|
function($row) {
|
|
return $row['value'];
|
|
},
|
|
$obj->fields+$obj->adhoc
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Build query string and arguments
|
|
* @return array
|
|
* @param $fields string
|
|
* @param $filter string|array
|
|
* @param $options array
|
|
**/
|
|
function stringify($fields,$filter=NULL,array $options=NULL) {
|
|
if (!$options)
|
|
$options=[];
|
|
$options+=[
|
|
'group'=>NULL,
|
|
'order'=>NULL,
|
|
'limit'=>0,
|
|
'offset'=>0,
|
|
'comment'=>NULL
|
|
];
|
|
$db=$this->db;
|
|
$sql='SELECT '.$fields.' FROM '.$this->table;
|
|
if (isset($this->as))
|
|
$sql.=' AS '.$this->db->quotekey($this->as);
|
|
$args=[];
|
|
if (is_array($filter)) {
|
|
$args=isset($filter[1]) && is_array($filter[1])?
|
|
$filter[1]:
|
|
array_slice($filter,1,NULL,TRUE);
|
|
$args=is_array($args)?$args:[1=>$args];
|
|
list($filter)=$filter;
|
|
}
|
|
if ($filter)
|
|
$sql.=' WHERE '.$filter;
|
|
if ($options['group']) {
|
|
$sql.=' GROUP BY '.implode(',',array_map(
|
|
function($str) use($db) {
|
|
return preg_replace_callback(
|
|
'/\b(\w+[._\-\w]*)\h*(HAVING.+|$)/i',
|
|
function($parts) use($db) {
|
|
return $db->quotekey($parts[1]).
|
|
(isset($parts[2])?(' '.$parts[2]):'');
|
|
},
|
|
$str
|
|
);
|
|
},
|
|
explode(',',$options['group'])));
|
|
}
|
|
if ($options['order']) {
|
|
$char=substr($db->quotekey(''),0,1);// quoting char
|
|
$order=' ORDER BY '.(is_bool(strpos($options['order'],$char))?
|
|
implode(',',array_map(function($str) use($db) {
|
|
return preg_match('/^\h*(\w+[._\-\w]*)'.
|
|
'(?:\h+((?:ASC|DESC)[\w\h]*))?\h*$/i',
|
|
$str,$parts)?
|
|
($db->quotekey($parts[1]).
|
|
(isset($parts[2])?(' '.$parts[2]):'')):$str;
|
|
},explode(',',$options['order']))):
|
|
$options['order']);
|
|
}
|
|
// SQL Server fixes
|
|
if (preg_match('/mssql|sqlsrv|odbc/', $this->engine) &&
|
|
($options['limit'] || $options['offset'])) {
|
|
// order by pkey when no ordering option was given
|
|
if (!$options['order'])
|
|
foreach ($this->fields as $key=>$field)
|
|
if ($field['pkey']) {
|
|
$order=' ORDER BY '.$db->quotekey($key);
|
|
break;
|
|
}
|
|
$ofs=$options['offset']?(int)$options['offset']:0;
|
|
$lmt=$options['limit']?(int)$options['limit']:0;
|
|
if (strncmp($db->version(),'11',2)>=0) {
|
|
// SQL Server >= 2012
|
|
$sql.=$order.' OFFSET '.$ofs.' ROWS';
|
|
if ($lmt)
|
|
$sql.=' FETCH NEXT '.$lmt.' ROWS ONLY';
|
|
}
|
|
else {
|
|
// SQL Server 2008
|
|
$sql=preg_replace('/SELECT/',
|
|
'SELECT '.
|
|
($lmt>0?'TOP '.($ofs+$lmt):'').' ROW_NUMBER() '.
|
|
'OVER ('.$order.') AS rnum,',$sql.$order,1);
|
|
$sql='SELECT * FROM ('.$sql.') x WHERE rnum > '.($ofs);
|
|
}
|
|
}
|
|
else {
|
|
if (isset($order))
|
|
$sql.=$order;
|
|
if ($options['limit'])
|
|
$sql.=' LIMIT '.(int)$options['limit'];
|
|
if ($options['offset'])
|
|
$sql.=' OFFSET '.(int)$options['offset'];
|
|
}
|
|
if ($options['comment'])
|
|
$sql.="\n".' /* '.$options['comment'].' */';
|
|
return [$sql,$args];
|
|
}
|
|
|
|
/**
|
|
* Build query string and execute
|
|
* @return static[]
|
|
* @param $fields string
|
|
* @param $filter string|array
|
|
* @param $options array
|
|
* @param $ttl int|array
|
|
**/
|
|
function select($fields,$filter=NULL,array $options=NULL,$ttl=0) {
|
|
list($sql,$args)=$this->stringify($fields,$filter,$options);
|
|
$result=$this->db->exec($sql,$args,$ttl);
|
|
$out=[];
|
|
foreach ($result as &$row) {
|
|
foreach ($row as $field=>&$val) {
|
|
if (array_key_exists($field,$this->fields)) {
|
|
if (!is_null($val) || !$this->fields[$field]['nullable'])
|
|
$val=$this->db->value(
|
|
$this->fields[$field]['pdo_type'],$val);
|
|
}
|
|
unset($val);
|
|
}
|
|
$out[]=$this->factory($row);
|
|
unset($row);
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Return records that match criteria
|
|
* @return static[]
|
|
* @param $filter string|array
|
|
* @param $options array
|
|
* @param $ttl int|array
|
|
**/
|
|
function find($filter=NULL,array $options=NULL,$ttl=0) {
|
|
if (!$options)
|
|
$options=[];
|
|
$options+=[
|
|
'group'=>NULL,
|
|
'order'=>NULL,
|
|
'limit'=>0,
|
|
'offset'=>0
|
|
];
|
|
$adhoc='';
|
|
foreach ($this->adhoc as $key=>$field)
|
|
$adhoc.=','.$field['expr'].' AS '.$this->db->quotekey($key);
|
|
return $this->select(
|
|
($options['group'] && !preg_match('/mysql|sqlite/',$this->engine)?
|
|
$options['group']:
|
|
implode(',',array_map([$this->db,'quotekey'],
|
|
array_keys($this->fields)))).$adhoc,$filter,$options,$ttl);
|
|
}
|
|
|
|
/**
|
|
* Count records that match criteria
|
|
* @return int
|
|
* @param $filter string|array
|
|
* @param $options array
|
|
* @param $ttl int|array
|
|
**/
|
|
function count($filter=NULL,array $options=NULL,$ttl=0) {
|
|
$adhoc=[];
|
|
// with grouping involved, we need to wrap the actualy query and count the results
|
|
if ($subquery_mode=($options && !empty($options['group']))) {
|
|
$group_string=preg_replace('/HAVING.+$/i','',$options['group']);
|
|
$group_fields=array_flip(array_map('trim',explode(',',$group_string)));
|
|
foreach ($this->adhoc as $key=>$field)
|
|
// add adhoc fields that are used for grouping
|
|
if (isset($group_fields[$key]))
|
|
$adhoc[]=$field['expr'].' AS '.$this->db->quotekey($key);
|
|
$fields=implode(',',$adhoc);
|
|
if (empty($fields))
|
|
// Select at least one field, ideally the grouping fields
|
|
// or sqlsrv fails
|
|
$fields=$group_string;
|
|
if (preg_match('/mssql|dblib|sqlsrv/',$this->engine))
|
|
$fields='TOP 100 PERCENT '.$fields;
|
|
} else {
|
|
// for simple count just add a new adhoc counter
|
|
$fields='COUNT(*) AS '.$this->db->quotekey('_rows');
|
|
}
|
|
list($sql,$args)=$this->stringify($fields,$filter,$options);
|
|
if ($subquery_mode)
|
|
$sql='SELECT COUNT(*) AS '.$this->db->quotekey('_rows').' '.
|
|
'FROM ('.$sql.') AS '.$this->db->quotekey('_temp');
|
|
$result=$this->db->exec($sql,$args,$ttl);
|
|
unset($this->adhoc['_rows']);
|
|
return (int)$result[0]['_rows'];
|
|
}
|
|
/**
|
|
* Return record at specified offset using same criteria as
|
|
* previous load() call and make it active
|
|
* @return static
|
|
* @param $ofs int
|
|
**/
|
|
function skip($ofs=1) {
|
|
$out=parent::skip($ofs);
|
|
$dry=$this->dry();
|
|
foreach ($this->fields as $key=>&$field) {
|
|
$field['value']=$dry?NULL:$out->fields[$key]['value'];
|
|
$field['initial']=$field['value'];
|
|
$field['changed']=FALSE;
|
|
if ($field['pkey'])
|
|
$field['previous']=$dry?NULL:$out->fields[$key]['value'];
|
|
unset($field);
|
|
}
|
|
foreach ($this->adhoc as $key=>&$field) {
|
|
$field['value']=$dry?NULL:$out->adhoc[$key]['value'];
|
|
unset($field);
|
|
}
|
|
if (!$dry && isset($this->trigger['load']))
|
|
\Base::instance()->call($this->trigger['load'],$this);
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Insert new record
|
|
* @return static
|
|
**/
|
|
function insert() {
|
|
$args=[];
|
|
$actr=0;
|
|
$nctr=0;
|
|
$fields='';
|
|
$values='';
|
|
$filter='';
|
|
$pkeys=[];
|
|
$nkeys=[];
|
|
$ckeys=[];
|
|
$inc=NULL;
|
|
foreach ($this->fields as $key=>$field)
|
|
if ($field['pkey'])
|
|
$pkeys[$key]=$field['previous'];
|
|
if (isset($this->trigger['beforeinsert']) &&
|
|
\Base::instance()->call($this->trigger['beforeinsert'],
|
|
[$this,$pkeys])===FALSE)
|
|
return $this;
|
|
if ($this->valid())
|
|
// duplicate record
|
|
foreach ($this->fields as $key=>&$field) {
|
|
$field['changed']=true;
|
|
if ($field['pkey'] && !$inc && $field['pdo_type']==\PDO::PARAM_INT
|
|
&& !$field['nullable'])
|
|
$inc=$key;
|
|
unset($field);
|
|
}
|
|
foreach ($this->fields as $key=>&$field) {
|
|
if ($field['pkey']) {
|
|
$field['previous']=$field['value'];
|
|
if (!$inc && $field['pdo_type']==\PDO::PARAM_INT &&
|
|
empty($field['value']) && !$field['nullable'] &&
|
|
is_null($field['default']))
|
|
$inc=$key;
|
|
$filter.=($filter?' AND ':'').$this->db->quotekey($key).'=?';
|
|
$nkeys[$nctr+1]=[$field['value'],$field['pdo_type']];
|
|
$nctr++;
|
|
}
|
|
if ($field['changed'] && $key!=$inc) {
|
|
$fields.=($actr?',':'').$this->db->quotekey($key);
|
|
$values.=($actr?',':'').'?';
|
|
$args[$actr+1]=[$field['value'],$field['pdo_type']];
|
|
$actr++;
|
|
$ckeys[]=$key;
|
|
}
|
|
unset($field);
|
|
}
|
|
if ($fields) {
|
|
$add=$aik='';
|
|
if ($this->engine=='pgsql' && !empty($pkeys)) {
|
|
$names=array_keys($pkeys);
|
|
$aik=end($names);
|
|
$add=' RETURNING '.$this->db->quotekey($aik);
|
|
}
|
|
$lID=$this->db->exec(
|
|
(preg_match('/mssql|dblib|sqlsrv/',$this->engine) &&
|
|
array_intersect(array_keys($pkeys),$ckeys)?
|
|
'SET IDENTITY_INSERT '.$this->table.' ON;':'').
|
|
'INSERT INTO '.$this->table.' ('.$fields.') '.
|
|
'VALUES ('.$values.')'.$add,$args
|
|
);
|
|
if ($this->engine=='pgsql' && $lID && $aik)
|
|
$this->_id=$lID[0][$aik];
|
|
elseif ($this->engine!='oci')
|
|
$this->_id=$this->db->lastinsertid();
|
|
// Reload to obtain default and auto-increment field values
|
|
if ($reload=(($inc && $this->_id) || $filter))
|
|
$this->load($inc?
|
|
[$inc.'=?',$this->db->value(
|
|
$this->fields[$inc]['pdo_type'],$this->_id)]:
|
|
[$filter,$nkeys]);
|
|
if (isset($this->trigger['afterinsert']))
|
|
\Base::instance()->call($this->trigger['afterinsert'],
|
|
[$this,$pkeys]);
|
|
// reset changed flag after calling afterinsert
|
|
if (!$reload)
|
|
foreach ($this->fields as $key=>&$field) {
|
|
$field['changed']=FALSE;
|
|
$field['initial']=$field['value'];
|
|
unset($field);
|
|
}
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Update current record
|
|
* @return static
|
|
**/
|
|
function update() {
|
|
$args=[];
|
|
$ctr=0;
|
|
$pairs='';
|
|
$pkeys=[];
|
|
foreach ($this->fields as $key=>$field)
|
|
if ($field['pkey'])
|
|
$pkeys[$key]=$field['previous'];
|
|
if (isset($this->trigger['beforeupdate']) &&
|
|
\Base::instance()->call($this->trigger['beforeupdate'],
|
|
[$this,$pkeys])===FALSE)
|
|
return $this;
|
|
foreach ($this->fields as $key=>$field)
|
|
if ($field['changed']) {
|
|
$pairs.=($pairs?',':'').$this->db->quotekey($key).'=?';
|
|
$args[++$ctr]=[$field['value'],$field['pdo_type']];
|
|
}
|
|
if ($pairs) {
|
|
$filter='';
|
|
foreach ($this->fields as $key=>$field)
|
|
if ($field['pkey']) {
|
|
$filter.=($filter?' AND ':' WHERE ').
|
|
$this->db->quotekey($key).'=?';
|
|
$args[++$ctr]=[$field['previous'],$field['pdo_type']];
|
|
}
|
|
if (!$filter)
|
|
user_error(sprintf(self::E_PKey,$this->source),E_USER_ERROR);
|
|
$sql='UPDATE '.$this->table.' SET '.$pairs.$filter;
|
|
$this->db->exec($sql,$args);
|
|
}
|
|
if (isset($this->trigger['afterupdate']))
|
|
\Base::instance()->call($this->trigger['afterupdate'],
|
|
[$this,$pkeys]);
|
|
// reset changed flag after calling afterupdate
|
|
foreach ($this->fields as $key=>&$field) {
|
|
$field['changed']=FALSE;
|
|
$field['initial']=$field['value'];
|
|
unset($field);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* batch-update multiple records at once
|
|
* @param string|array $filter
|
|
* @return int
|
|
*/
|
|
function updateAll($filter=NULL) {
|
|
$args=[];
|
|
$ctr=$out=0;
|
|
$pairs='';
|
|
foreach ($this->fields as $key=>$field)
|
|
if ($field['changed']) {
|
|
$pairs.=($pairs?',':'').$this->db->quotekey($key).'=?';
|
|
$args[++$ctr]=[$field['value'],$field['pdo_type']];
|
|
}
|
|
if ($filter)
|
|
if (is_array($filter)) {
|
|
$cond=array_shift($filter);
|
|
$args=array_merge($args,$filter);
|
|
$filter=' WHERE '.$cond;
|
|
} else
|
|
$filter=' WHERE '.$filter;
|
|
if ($pairs) {
|
|
$sql='UPDATE '.$this->table.' SET '.$pairs.$filter;
|
|
$out = $this->db->exec($sql,$args);
|
|
}
|
|
// reset changed flag after calling afterupdate
|
|
foreach ($this->fields as $key=>&$field) {
|
|
$field['changed']=FALSE;
|
|
$field['initial']=$field['value'];
|
|
unset($field);
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
|
|
/**
|
|
* Delete current record
|
|
* @return int
|
|
* @param $quick bool
|
|
* @param $filter string|array
|
|
**/
|
|
function erase($filter=NULL,$quick=TRUE) {
|
|
if (isset($filter)) {
|
|
if (!$quick) {
|
|
$out=0;
|
|
foreach ($this->find($filter) as $mapper)
|
|
$out+=$mapper->erase();
|
|
return $out;
|
|
}
|
|
$args=[];
|
|
if (is_array($filter)) {
|
|
$args=isset($filter[1]) && is_array($filter[1])?
|
|
$filter[1]:
|
|
array_slice($filter,1,NULL,TRUE);
|
|
$args=is_array($args)?$args:[1=>$args];
|
|
list($filter)=$filter;
|
|
}
|
|
return $this->db->
|
|
exec('DELETE FROM '.$this->table.
|
|
($filter?' WHERE '.$filter:'').';',$args);
|
|
}
|
|
$args=[];
|
|
$ctr=0;
|
|
$filter='';
|
|
$pkeys=[];
|
|
foreach ($this->fields as $key=>&$field) {
|
|
if ($field['pkey']) {
|
|
$filter.=($filter?' AND ':'').$this->db->quotekey($key).'=?';
|
|
$args[$ctr+1]=[$field['previous'],$field['pdo_type']];
|
|
$pkeys[$key]=$field['previous'];
|
|
$ctr++;
|
|
}
|
|
$field['value']=NULL;
|
|
$field['changed']=(bool)$field['default'];
|
|
if ($field['pkey'])
|
|
$field['previous']=NULL;
|
|
unset($field);
|
|
}
|
|
if (!$filter)
|
|
user_error(sprintf(self::E_PKey,$this->source),E_USER_ERROR);
|
|
foreach ($this->adhoc as &$field) {
|
|
$field['value']=NULL;
|
|
unset($field);
|
|
}
|
|
parent::erase();
|
|
if (isset($this->trigger['beforeerase']) &&
|
|
\Base::instance()->call($this->trigger['beforeerase'],
|
|
[$this,$pkeys])===FALSE)
|
|
return 0;
|
|
$out=$this->db->
|
|
exec('DELETE FROM '.$this->table.' WHERE '.$filter.';',$args);
|
|
if (isset($this->trigger['aftererase']))
|
|
\Base::instance()->call($this->trigger['aftererase'],
|
|
[$this,$pkeys]);
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Reset cursor
|
|
* @return NULL
|
|
**/
|
|
function reset() {
|
|
foreach ($this->fields as &$field) {
|
|
$field['value']=NULL;
|
|
$field['initial']=NULL;
|
|
$field['changed']=FALSE;
|
|
if ($field['pkey'])
|
|
$field['previous']=NULL;
|
|
unset($field);
|
|
}
|
|
foreach ($this->adhoc as &$field) {
|
|
$field['value']=NULL;
|
|
unset($field);
|
|
}
|
|
parent::reset();
|
|
}
|
|
|
|
/**
|
|
* Hydrate mapper object using hive array variable
|
|
* @return NULL
|
|
* @param $var array|string
|
|
* @param $func callback
|
|
**/
|
|
function copyfrom($var,$func=NULL) {
|
|
if (is_string($var))
|
|
$var=\Base::instance()->$var;
|
|
if ($func)
|
|
$var=call_user_func($func,$var);
|
|
foreach ($var as $key=>$val)
|
|
if (in_array($key,array_keys($this->fields)))
|
|
$this->set($key,$val);
|
|
}
|
|
|
|
/**
|
|
* Populate hive array variable with mapper fields
|
|
* @return NULL
|
|
* @param $key string
|
|
**/
|
|
function copyto($key) {
|
|
$var=&\Base::instance()->ref($key);
|
|
foreach ($this->fields+$this->adhoc as $key=>$field)
|
|
$var[$key]=$field['value'];
|
|
}
|
|
|
|
/**
|
|
* Return schema and, if the first argument is provided, update it
|
|
* @return array
|
|
* @param $fields NULL|array
|
|
**/
|
|
function schema($fields=null) {
|
|
if ($fields)
|
|
$this->fields = $fields;
|
|
return $this->fields;
|
|
}
|
|
|
|
/**
|
|
* Return field names
|
|
* @return array
|
|
* @param $adhoc bool
|
|
**/
|
|
function fields($adhoc=TRUE) {
|
|
return array_keys($this->fields+($adhoc?$this->adhoc:[]));
|
|
}
|
|
|
|
/**
|
|
* Return TRUE if field is not nullable
|
|
* @return bool
|
|
* @param $field string
|
|
**/
|
|
function required($field) {
|
|
return isset($this->fields[$field]) &&
|
|
!$this->fields[$field]['nullable'];
|
|
}
|
|
|
|
/**
|
|
* Retrieve external iterator for fields
|
|
* @return object
|
|
**/
|
|
function getiterator() {
|
|
return new \ArrayIterator($this->cast());
|
|
}
|
|
|
|
/**
|
|
* Assign alias for table
|
|
* @param $alias string
|
|
**/
|
|
function alias($alias) {
|
|
$this->as=$alias;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Instantiate class
|
|
* @param $db \DB\SQL
|
|
* @param $table string
|
|
* @param $fields array|string
|
|
* @param $ttl int|array
|
|
**/
|
|
function __construct(\DB\SQL $db,$table,$fields=NULL,$ttl=60) {
|
|
$this->db=$db;
|
|
$this->engine=$db->driver();
|
|
if ($this->engine=='oci')
|
|
$table=strtoupper($table);
|
|
$this->source=$table;
|
|
$this->table=$this->db->quotekey($table);
|
|
$this->fields=$db->schema($table,$fields,$ttl);
|
|
$this->reset();
|
|
}
|
|
|
|
}
|
|
|