2019-07-23 21:57:44 +08:00
|
|
|
<?php
|
|
|
|
namespace ZM;
|
|
|
|
require_once('database.php');
|
|
|
|
|
|
|
|
$object_cache = array();
|
|
|
|
|
|
|
|
class ZM_Object {
|
|
|
|
|
|
|
|
public function __construct($IdOrRow = NULL) {
|
|
|
|
$class = get_class($this);
|
|
|
|
|
|
|
|
$row = NULL;
|
|
|
|
if ( $IdOrRow ) {
|
|
|
|
if ( is_integer($IdOrRow) or ctype_digit($IdOrRow) ) {
|
2019-08-16 04:04:56 +08:00
|
|
|
$table = $class::$table;
|
2019-07-23 21:57:44 +08:00
|
|
|
$row = dbFetchOne("SELECT * FROM `$table` WHERE `Id`=?", NULL, array($IdOrRow));
|
|
|
|
if ( !$row ) {
|
|
|
|
Error("Unable to load $class record for Id=$IdOrRow");
|
|
|
|
}
|
|
|
|
} elseif ( is_array($IdOrRow) ) {
|
|
|
|
$row = $IdOrRow;
|
|
|
|
}
|
2019-08-16 04:04:56 +08:00
|
|
|
|
|
|
|
if ( $row ) {
|
|
|
|
global $object_cache;
|
|
|
|
if ( ! isset($object_cache[$class]) ) {
|
|
|
|
$object_cache[$class] = array();
|
|
|
|
}
|
|
|
|
$cache = &$object_cache[$class];
|
|
|
|
|
|
|
|
foreach ($row as $k => $v) {
|
|
|
|
$this->{$k} = $v;
|
|
|
|
}
|
|
|
|
$cache[$row['Id']] = $this;
|
2019-07-23 21:57:44 +08:00
|
|
|
}
|
2019-08-16 04:04:56 +08:00
|
|
|
} # end if isset($IdOrRow)
|
|
|
|
} # end function __construct
|
2019-07-23 21:57:44 +08:00
|
|
|
|
|
|
|
public function __call($fn, array $args){
|
2019-09-20 02:55:45 +08:00
|
|
|
$type = (array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn])) ? $this->defaults[$fn]['type'] : 'scalar';
|
2019-07-23 21:57:44 +08:00
|
|
|
if ( count($args) ) {
|
2019-09-20 02:55:45 +08:00
|
|
|
if ( $type == 'set' and is_array($args[0]) )
|
|
|
|
$this->{$fn} = implode(',', $args[0]);
|
|
|
|
else
|
|
|
|
$this->{$fn} = $args[0];
|
2019-07-23 21:57:44 +08:00
|
|
|
}
|
2019-09-20 02:55:45 +08:00
|
|
|
|
2019-12-08 00:45:32 +08:00
|
|
|
if ( property_exists($this, $fn) ) {
|
2019-07-23 21:57:44 +08:00
|
|
|
return $this->{$fn};
|
|
|
|
} else {
|
|
|
|
if ( array_key_exists($fn, $this->defaults) ) {
|
2019-09-20 02:55:45 +08:00
|
|
|
if ( is_array($this->defaults[$fn]) ) {
|
|
|
|
return $this->defaults[$fn]['default'];
|
|
|
|
}
|
|
|
|
return $this->defaults[$fn];
|
2019-07-23 21:57:44 +08:00
|
|
|
} else {
|
|
|
|
$backTrace = debug_backtrace();
|
2019-08-16 04:04:56 +08:00
|
|
|
Warning("Unknown function call Object->$fn from ".print_r($backTrace,true));
|
2019-07-23 21:57:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function _find($class, $parameters = null, $options = null ) {
|
|
|
|
$table = $class::$table;
|
|
|
|
$filters = array();
|
|
|
|
$sql = "SELECT * FROM `$table` ";
|
|
|
|
$values = array();
|
|
|
|
|
|
|
|
if ( $parameters ) {
|
|
|
|
$fields = array();
|
|
|
|
$sql .= 'WHERE ';
|
|
|
|
foreach ( $parameters as $field => $value ) {
|
|
|
|
if ( $value == null ) {
|
|
|
|
$fields[] = '`'.$field.'` IS NULL';
|
|
|
|
} else if ( is_array($value) ) {
|
|
|
|
$func = function(){return '?';};
|
|
|
|
$fields[] = '`'.$field.'` IN ('.implode(',', array_map($func, $value)). ')';
|
|
|
|
$values += $value;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
$fields[] = '`'.$field.'`=?';
|
|
|
|
$values[] = $value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$sql .= implode(' AND ', $fields );
|
|
|
|
}
|
|
|
|
if ( $options ) {
|
|
|
|
if ( isset($options['order']) ) {
|
|
|
|
$sql .= ' ORDER BY ' . $options['order'];
|
|
|
|
}
|
|
|
|
if ( isset($options['limit']) ) {
|
|
|
|
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
|
|
|
|
$sql .= ' LIMIT ' . $options['limit'];
|
|
|
|
} else {
|
2019-09-27 04:26:28 +08:00
|
|
|
$backTrace = debug_backtrace();
|
2019-07-23 21:57:44 +08:00
|
|
|
Error('Invalid value for limit('.$options['limit'].') passed to '.get_class()."::find from ".print_r($backTrace,true));
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$rows = dbFetchAll($sql, NULL, $values);
|
|
|
|
$results = array();
|
|
|
|
if ( $rows ) {
|
|
|
|
foreach ( $rows as $row ) {
|
|
|
|
array_push($results , new $class($row));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $results;
|
2019-08-16 04:04:56 +08:00
|
|
|
} # end public function _find()
|
2019-07-23 21:57:44 +08:00
|
|
|
|
|
|
|
public static function _find_one($class, $parameters = array(), $options = array() ) {
|
|
|
|
global $object_cache;
|
2020-01-11 01:44:59 +08:00
|
|
|
if ( ! isset($object_cache[$class]) ) {
|
2019-07-23 21:57:44 +08:00
|
|
|
$object_cache[$class] = array();
|
2020-01-11 01:44:59 +08:00
|
|
|
}
|
2019-08-16 04:04:56 +08:00
|
|
|
$cache = &$object_cache[$class];
|
2019-07-23 21:57:44 +08:00
|
|
|
if (
|
|
|
|
( count($parameters) == 1 ) and
|
|
|
|
isset($parameters['Id']) and
|
|
|
|
isset($cache[$parameters['Id']]) ) {
|
|
|
|
return $cache[$parameters['Id']];
|
|
|
|
}
|
|
|
|
$options['limit'] = 1;
|
|
|
|
$results = ZM_Object::_find($class, $parameters, $options);
|
|
|
|
if ( ! sizeof($results) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return $results[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function Objects_Indexed_By_Id($class) {
|
|
|
|
$results = array();
|
|
|
|
foreach ( ZM_Object::_find($class, null, array('order'=>'lower(Name)')) as $Object ) {
|
|
|
|
$results[$Object->Id()] = $Object;
|
|
|
|
}
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function to_json() {
|
|
|
|
$json = array();
|
|
|
|
foreach ($this->defaults as $key => $value) {
|
|
|
|
if ( is_callable(array($this, $key)) ) {
|
|
|
|
$json[$key] = $this->$key();
|
2019-12-08 00:45:32 +08:00
|
|
|
} else if ( property_exists($this, $key) ) {
|
2019-07-23 21:57:44 +08:00
|
|
|
$json[$key] = $this->{$key};
|
|
|
|
} else {
|
2019-12-08 00:45:32 +08:00
|
|
|
$json[$key] = $this->defaults[$key];
|
2019-07-23 21:57:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return json_encode($json);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function set($data) {
|
|
|
|
foreach ( $data as $k => $v ) {
|
|
|
|
if ( method_exists($this, $k) ) {
|
|
|
|
$this->{$k}($v);
|
|
|
|
} else {
|
|
|
|
if ( is_array($v) ) {
|
|
|
|
# perhaps should turn into a comma-separated string
|
|
|
|
$this->{$k} = implode(',', $v);
|
|
|
|
} else if ( is_string($v) ) {
|
2019-12-19 08:03:37 +08:00
|
|
|
if ( 0 ) {
|
|
|
|
# Remarking this out. We are setting a value, not asking for a default to be set.
|
|
|
|
# So don't do defaults here, do them somewhere else
|
|
|
|
if ( ($v == null) and array_key_exists($k, $this->defaults) ) {
|
|
|
|
Logger::Debug("$k => Have default for $v: ");
|
|
|
|
if ( is_array($this->defaults[$k]) ) {
|
2019-09-20 02:55:45 +08:00
|
|
|
$this->{$k} = $this->defaults[$k]['default'];
|
2019-12-19 08:03:37 +08:00
|
|
|
} else {
|
|
|
|
$this->{$k} = $this->defaults[$k];
|
|
|
|
Logger::Debug("$k => Have default for $v: " . $this->{$k});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$this->{$k} = trim($v);
|
2019-07-23 21:57:44 +08:00
|
|
|
}
|
2019-12-19 08:03:37 +08:00
|
|
|
} else {
|
|
|
|
$this->{$k} = trim($v);
|
|
|
|
}
|
|
|
|
|
2019-07-23 21:57:44 +08:00
|
|
|
} else if ( is_integer($v) ) {
|
|
|
|
$this->{$k} = $v;
|
|
|
|
} else if ( is_bool($v) ) {
|
|
|
|
$this->{$k} = $v;
|
|
|
|
} else if ( is_null($v) ) {
|
|
|
|
$this->{$k} = $v;
|
|
|
|
} else {
|
2019-08-16 04:04:56 +08:00
|
|
|
Error("Unknown type $k => $v of var " . gettype($v));
|
2019-07-23 21:57:44 +08:00
|
|
|
$this->{$k} = $v;
|
|
|
|
}
|
|
|
|
} # end if method_exists
|
|
|
|
} # end foreach $data as $k=>$v
|
2019-09-20 02:55:45 +08:00
|
|
|
} # end function set($data)
|
2019-07-23 21:57:44 +08:00
|
|
|
|
2019-09-20 02:55:45 +08:00
|
|
|
/* types is an array of fields telling use that the input might be a checkbox so not present in the input, but therefore has a value
|
|
|
|
*/
|
|
|
|
public function changes($new_values, $defaults=null) {
|
2019-07-23 21:57:44 +08:00
|
|
|
$changes = array();
|
2019-09-20 02:55:45 +08:00
|
|
|
|
|
|
|
if ( $defaults ) {
|
|
|
|
foreach ( $defaults as $field => $type ) {
|
|
|
|
if ( isset($new_values[$field]) ) {
|
|
|
|
# Will have already been handled above
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-12-03 01:30:03 +08:00
|
|
|
if ( isset($this->defaults[$field]) ) {
|
2019-09-20 02:55:45 +08:00
|
|
|
if ( is_array($this->defaults[$field]) ) {
|
|
|
|
$new_values[$field] = $this->defaults[$field]['default'];
|
|
|
|
} else {
|
|
|
|
$new_values[$field] = $this->defaults[$field];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} # end foreach default
|
|
|
|
}
|
2019-07-24 23:24:14 +08:00
|
|
|
foreach ( $new_values as $field => $value ) {
|
|
|
|
|
|
|
|
if ( method_exists($this, $field) ) {
|
|
|
|
$old_value = $this->$field();
|
2019-08-16 04:04:56 +08:00
|
|
|
Logger::Debug("Checking method $field () ".print_r($old_value,true).' => ' . print_r($value,true));
|
2019-07-24 23:24:14 +08:00
|
|
|
if ( is_array($old_value) ) {
|
|
|
|
$diff = array_recursive_diff($old_value, $value);
|
2019-09-20 02:55:45 +08:00
|
|
|
Logger::Debug("Checking method $field () diff isi ".print_r($diff,true));
|
2019-07-24 23:24:14 +08:00
|
|
|
if ( count($diff) ) {
|
|
|
|
$changes[$field] = $value;
|
|
|
|
}
|
|
|
|
} else if ( $this->$field() != $value ) {
|
|
|
|
$changes[$field] = $value;
|
|
|
|
}
|
2019-12-08 00:45:32 +08:00
|
|
|
} else if ( property_exists($this, $field) ) {
|
2019-09-20 02:55:45 +08:00
|
|
|
$type = (array_key_exists($field, $this->defaults) && is_array($this->defaults[$field])) ? $this->defaults[$field]['type'] : 'scalar';
|
|
|
|
Logger::Debug("Checking field $field => current ".
|
|
|
|
(is_array($this->{$field}) ? implode(',',$this->{$field}) : $this->{$field}) . ' ?= ' .
|
|
|
|
(is_array($value) ? implode(',', $value) : $value)
|
|
|
|
);
|
|
|
|
if ( $type == 'set' ) {
|
|
|
|
$old_value = is_array($this->$field) ? $this->$field : explode(',', $this->$field);
|
|
|
|
$new_value = is_array($value) ? $value : explode(',', $value);
|
|
|
|
|
|
|
|
$diff = array_recursive_diff($old_value, $new_value);
|
|
|
|
Logger::Debug("Checking value $field () diff isi ".print_r($diff,true));
|
|
|
|
if ( count($diff) ) {
|
|
|
|
$changes[$field] = $new_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Input might be a command separated string, or an array
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if ( $this->{$field} != $value ) {
|
|
|
|
$changes[$field] = $value;
|
|
|
|
}
|
2019-07-24 23:24:14 +08:00
|
|
|
}
|
|
|
|
} else if ( array_key_exists($field, $this->defaults) ) {
|
2019-09-20 02:55:45 +08:00
|
|
|
if ( is_array($this->defaults[$field]) ) {
|
|
|
|
$default = $this->defaults[$field]['default'];
|
|
|
|
} else {
|
|
|
|
$default = $this->defaults[$field];
|
|
|
|
}
|
2019-07-24 23:24:14 +08:00
|
|
|
|
2019-09-20 02:55:45 +08:00
|
|
|
Logger::Debug("Checking default $field => ".
|
|
|
|
( is_array($default) ? implode(',',$default) : $default).
|
|
|
|
' ' .
|
|
|
|
( is_array($value) ? implode(',', $value) : $value)
|
|
|
|
);
|
|
|
|
if ( $default != $value ) {
|
2019-07-24 23:24:14 +08:00
|
|
|
$changes[$field] = $value;
|
2019-07-23 21:57:44 +08:00
|
|
|
}
|
|
|
|
}
|
2019-07-24 23:24:14 +08:00
|
|
|
|
|
|
|
#if ( (!array_key_exists($field, $this)) or ( $this->{$field} != $new_values[$field] ) ) {
|
|
|
|
#Logger::Debug("Checking default $field => $default_value changes becaause" . $new_values[$field].' != '.$new_values[$field]);
|
|
|
|
#$changes[$field] = $new_values[$field];
|
|
|
|
##} else if {
|
|
|
|
#Logger::Debug("Checking default $field => $default_value changes becaause " . $new_values[$field].' != '.$new_values[$field]);
|
|
|
|
##array_push( $changes, [$field=>$defaults[$field]] );
|
|
|
|
#}
|
|
|
|
#} else {
|
|
|
|
#Logger::Debug("Checking default $field => $default_value not in new_values");
|
|
|
|
#}
|
2019-09-20 02:55:45 +08:00
|
|
|
} # end foreach newvalue
|
|
|
|
|
|
|
|
|
2019-07-23 21:57:44 +08:00
|
|
|
return $changes;
|
|
|
|
} # end public function changes
|
|
|
|
|
|
|
|
public function save($new_values = null) {
|
|
|
|
$class = get_class($this);
|
|
|
|
$table = $class::$table;
|
|
|
|
|
|
|
|
if ( $new_values ) {
|
2019-09-20 02:55:45 +08:00
|
|
|
//Logger::Debug("New values" . print_r($new_values, true));
|
2019-07-23 21:57:44 +08:00
|
|
|
$this->set($new_values);
|
|
|
|
}
|
|
|
|
|
2020-01-11 01:44:59 +08:00
|
|
|
foreach ( $this->defaults as $field => $default ) {
|
|
|
|
if ( (!array_key_exists($field, $this)) or empty($this->{$field}) ) {
|
|
|
|
if ( is_array($default) ) {
|
|
|
|
$this->{$field} = $default['default'];
|
|
|
|
} else {
|
|
|
|
$this->{$field} = $default;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-20 02:55:45 +08:00
|
|
|
$fields = array_filter(
|
|
|
|
$this->defaults,
|
|
|
|
function($v) {
|
|
|
|
return !(
|
|
|
|
is_array($v)
|
|
|
|
and
|
|
|
|
isset($v['do_not_update'])
|
|
|
|
and
|
|
|
|
$v['do_not_update']
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
$fields = array_keys($fields);
|
|
|
|
|
2019-07-23 21:57:44 +08:00
|
|
|
if ( $this->Id() ) {
|
2019-09-20 02:55:45 +08:00
|
|
|
$sql = 'UPDATE '.$table.' SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?';
|
|
|
|
$values = array_map(function($field){ return $this->{$field};}, $fields);
|
2019-07-23 21:57:44 +08:00
|
|
|
$values[] = $this->{'Id'};
|
|
|
|
if ( dbQuery($sql, $values) )
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
unset($fields['Id']);
|
|
|
|
|
2019-09-20 02:55:45 +08:00
|
|
|
$sql = 'INSERT INTO '.$table.
|
|
|
|
' ('.implode(', ', array_map(function($field) {return '`'.$field.'`';}, $fields)).
|
|
|
|
') VALUES ('.
|
|
|
|
implode(', ', array_map(function($field){return '?';}, $fields)).')';
|
|
|
|
|
|
|
|
$values = array_map(function($field){return $this->$field();}, $fields);
|
2019-07-23 21:57:44 +08:00
|
|
|
if ( dbQuery($sql, $values) ) {
|
|
|
|
$this->{'Id'} = dbInsertId();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
} // end function save
|
|
|
|
|
|
|
|
public function delete() {
|
|
|
|
$class = get_class($this);
|
|
|
|
$table = $class::$table;
|
|
|
|
dbQuery("DELETE FROM $table WHERE Id=?", array($this->{'Id'}));
|
|
|
|
if ( isset($object_cache[$class]) and isset($object_cache[$class][$this->{'Id'}]) )
|
|
|
|
unset($object_cache[$class][$this->{'Id'}]);
|
|
|
|
}
|
|
|
|
|
2019-09-04 22:07:08 +08:00
|
|
|
public function lock() {
|
|
|
|
$class = get_class($this);
|
|
|
|
$table = $class::$table;
|
|
|
|
$row = dbFetchOne("SELECT * FROM `$table` WHERE `Id`=?", NULL, array($this->Id()));
|
|
|
|
if ( !$row ) {
|
|
|
|
Error("Unable to lock $class record for Id=".$this->Id());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} # end class Object
|
2019-07-23 21:57:44 +08:00
|
|
|
?>
|