- updated SQL Schema module 2.2.0-dev -> 2.2.1
- updated Cortex DB mapper 1.4.2-dev -> 1.5.0-dev - improved location update function - improved system tooltip position, closed #461
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Cortex - a general purpose mapper for the PHP Fat-Free Framework
|
||||
* Cortex - the flexible data mapper for the PHP Fat-Free Framework
|
||||
*
|
||||
* The contents of this file are subject to the terms of the GNU General
|
||||
* Public License Version 3.0. You may not use this file except in
|
||||
@@ -13,13 +13,13 @@
|
||||
* | | < | <| -__|-- __|
|
||||
* |__|__|__||__|__|_____|_____|
|
||||
*
|
||||
* Copyright (c) 2016 by ikkez
|
||||
* Copyright (c) 2017 by ikkez
|
||||
* Christian Knuth <mail@ikkez.de>
|
||||
* https://github.com/ikkez/F3-Sugar/
|
||||
*
|
||||
* @package DB
|
||||
* @version 1.4.2-dev
|
||||
* @date 29.01.2016
|
||||
* @version 1.5.0-dev
|
||||
* @date 27.02.2017
|
||||
* @since 24.04.2012
|
||||
*/
|
||||
|
||||
@@ -53,7 +53,8 @@ class Cortex extends Cursor {
|
||||
$countFields, // relational counter buffer
|
||||
$preBinds, // bind values to be prepended to $filter
|
||||
$vFields, // virtual fields buffer
|
||||
$_ttl; // rel_ttl overwrite
|
||||
$_ttl, // rel_ttl overwrite
|
||||
$charset; // sql collation charset
|
||||
|
||||
/** @var Cursor */
|
||||
protected $mapper;
|
||||
@@ -61,8 +62,11 @@ class Cortex extends Cursor {
|
||||
/** @var CortexQueryParser */
|
||||
protected $queryParser;
|
||||
|
||||
static
|
||||
$init = false; // just init without mapper
|
||||
/** @var bool initialization flag */
|
||||
static $init = false;
|
||||
|
||||
/** @var array sql table schema cache */
|
||||
static $schema_cache = [];
|
||||
|
||||
const
|
||||
// special datatypes
|
||||
@@ -113,10 +117,10 @@ class Cortex extends Cursor {
|
||||
$this->table = $this->getTable();
|
||||
if (!$this->table)
|
||||
trigger_error(self::E_NO_TABLE,E_USER_ERROR);
|
||||
$this->ttl = $ttl ?: 60;
|
||||
$this->ttl = $ttl ?: ($this->ttl ?: 60);
|
||||
if (!$this->rel_ttl)
|
||||
$this->rel_ttl = 0;
|
||||
$this->_ttl = $this->rel_ttl ?: 0;
|
||||
$this->_ttl = $this->rel_ttl ?: ($this->rel_ttl ?: 0);
|
||||
if (static::$init == TRUE) return;
|
||||
if ($this->fluid)
|
||||
static::setup($this->db,$this->table,array());
|
||||
@@ -132,8 +136,10 @@ class Cortex extends Cursor {
|
||||
$this->mapper = new Jig\Mapper($this->db, $this->table);
|
||||
break;
|
||||
case 'sql':
|
||||
$this->mapper = new SQL\Mapper($this->db, $this->table, $this->whitelist,
|
||||
// ensure to load full table schema, so we can work with it at runtime
|
||||
$this->mapper = new SQL\Mapper($this->db, $this->table, null,
|
||||
($this->fluid)?0:$this->ttl);
|
||||
$this->applyWhitelist();
|
||||
break;
|
||||
case 'mongo':
|
||||
$this->mapper = new Mongo\Mapper($this->db, $this->table);
|
||||
@@ -163,23 +169,33 @@ class Cortex extends Cursor {
|
||||
* @return array
|
||||
*/
|
||||
public function fields(array $fields=array(), $exclude=false) {
|
||||
$addInc=[];
|
||||
if ($fields)
|
||||
// collect restricted fields for related mappers
|
||||
// collect & set restricted fields for related mappers
|
||||
foreach($fields as $i=>$val)
|
||||
if(is_int(strpos($val,'.'))) {
|
||||
list($key, $relField) = explode('.',$val,2);
|
||||
$this->relWhitelist[$key][(int)$exclude][] = $relField;
|
||||
unset($fields[$i]);
|
||||
$fields[] = $key;
|
||||
$addInc[] = $key;
|
||||
}
|
||||
$fields = array_unique($fields);
|
||||
$schema = $this->whitelist ?: $this->mapper->fields();
|
||||
if (!$schema && !$this->dbsType != 'sql' && $this->dry()) {
|
||||
if (!$schema && $this->dbsType != 'sql' && $this->dry()) {
|
||||
$schema = $this->load()->mapper->fields();
|
||||
$this->reset();
|
||||
}
|
||||
// include relation linkage fields to $fields (if $fields is a whitelist)
|
||||
if (!$exclude && !empty($fields) && !empty($addInc))
|
||||
$fields=array_unique(array_merge($fields,$addInc));
|
||||
// include relation linkage fields to existing whitelist (if $fields is a blacklist or there's nothing else to whitelist)
|
||||
elseif (!empty($addInc) && $this->whitelist)
|
||||
$this->whitelist=array_unique(array_merge($this->whitelist,$addInc));
|
||||
// initially merge configured fields into schema (add virtual/rel fields to schema)
|
||||
if (!$this->whitelist && $this->fieldConf)
|
||||
$schema=array_unique(array_merge($schema,array_keys($this->fieldConf)));
|
||||
$schema=array_unique(array_merge($schema,
|
||||
array_keys($this->fieldConf),array_keys($this->vFields?:[])));
|
||||
// skip if there's nothing to set for own model
|
||||
if (!$fields || empty($fields))
|
||||
return $schema;
|
||||
elseif ($exclude) {
|
||||
@@ -187,12 +203,32 @@ class Cortex extends Cursor {
|
||||
} else
|
||||
$this->whitelist=$fields;
|
||||
$id=$this->dbsType=='sql'?$this->primary:'_id';
|
||||
if(!in_array($id,$this->whitelist))
|
||||
if (!in_array($id,$this->whitelist))
|
||||
$this->whitelist[]=$id;
|
||||
$this->initMapper();
|
||||
$this->applyWhitelist();
|
||||
return $this->whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* apply whitelist to active mapper schema
|
||||
*/
|
||||
protected function applyWhitelist() {
|
||||
if ($this->dbsType == 'sql') {
|
||||
// fetch full schema
|
||||
if (!$this->fluid && isset(self::$schema_cache[$this->table]))
|
||||
$schema = self::$schema_cache[$this->table];
|
||||
else {
|
||||
$schema = $this->mapper->schema();
|
||||
self::$schema_cache[$this->table] = $schema;
|
||||
}
|
||||
// apply reduced fields schema
|
||||
if ($this->whitelist)
|
||||
$schema = array_intersect_key($schema, array_flip($this->whitelist));
|
||||
$this->mapper->schema($schema);
|
||||
$this->mapper->reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set model definition
|
||||
* config example:
|
||||
@@ -232,6 +268,7 @@ class Cortex extends Cursor {
|
||||
'db'=>$self->db,
|
||||
'fluid'=>$self->fluid,
|
||||
'primary'=>$self->primary,
|
||||
'charset'=>$self->charset,
|
||||
);
|
||||
unset($self);
|
||||
return $conf;
|
||||
@@ -292,54 +329,55 @@ class Cortex extends Cursor {
|
||||
if ($db instanceof SQL) {
|
||||
$schema = new Schema($db);
|
||||
// prepare field configuration
|
||||
if (!empty($fields))
|
||||
foreach($fields as $key => &$field) {
|
||||
// fetch relation field types
|
||||
$field = static::resolveRelationConf($field);
|
||||
// check m:m relation
|
||||
if (array_key_exists('has-many', $field)) {
|
||||
// m:m relation conf [class,to-key,from-key]
|
||||
if (is_array($relConf = $field['has-many'])) {
|
||||
$rel = $relConf[0]::resolveConfiguration();
|
||||
// check if foreign conf matches m:m
|
||||
if (array_key_exists($relConf[1],$rel['fieldConf'])
|
||||
&& !is_null($rel['fieldConf'][$relConf[1]])
|
||||
&& $relConf['hasRel'] == 'has-many') {
|
||||
// compute mm table name
|
||||
$mmTable = isset($relConf[2]) ? $relConf[2] :
|
||||
static::getMMTableName($rel['table'], $relConf['relField'],
|
||||
$table, $key, $rel['fieldConf'][$relConf[1]]['has-many']);
|
||||
if (!in_array($mmTable,$schema->getTables())) {
|
||||
$mmt = $schema->createTable($mmTable);
|
||||
$relField = $relConf['relField'].($relConf['isSelf']?'_ref':'');
|
||||
$mmt->addColumn($relField)->type($relConf['relFieldType']);
|
||||
$mmt->addColumn($key)->type($field['type']);
|
||||
$index = array($relField,$key);
|
||||
sort($index);
|
||||
$mmt->addIndex($index);
|
||||
$mmt->build();
|
||||
}
|
||||
foreach($fields as $key => &$field) {
|
||||
// fetch relation field types
|
||||
$field = static::resolveRelationConf($field);
|
||||
// check m:m relation
|
||||
if (array_key_exists('has-many', $field)) {
|
||||
// m:m relation conf [class,to-key,from-key]
|
||||
if (is_array($relConf = $field['has-many'])) {
|
||||
$rel = $relConf[0]::resolveConfiguration();
|
||||
// check if foreign conf matches m:m
|
||||
if (array_key_exists($relConf[1],$rel['fieldConf'])
|
||||
&& !is_null($rel['fieldConf'][$relConf[1]])
|
||||
&& $relConf['hasRel'] == 'has-many') {
|
||||
// compute mm table name
|
||||
$mmTable = isset($relConf[2]) ? $relConf[2] :
|
||||
static::getMMTableName($rel['table'], $relConf['relField'],
|
||||
$table, $key, $rel['fieldConf'][$relConf[1]]['has-many']);
|
||||
if (!in_array($mmTable,$schema->getTables())) {
|
||||
$mmt = $schema->createTable($mmTable);
|
||||
$relField = $relConf['relField'].($relConf['isSelf']?'_ref':'');
|
||||
$mmt->addColumn($relField)->type($relConf['relFieldType']);
|
||||
$mmt->addColumn($key)->type($field['type']);
|
||||
$index = array($relField,$key);
|
||||
sort($index);
|
||||
$mmt->addIndex($index);
|
||||
$mmt->build();
|
||||
}
|
||||
}
|
||||
unset($fields[$key]);
|
||||
continue;
|
||||
}
|
||||
// skip virtual fields with no type
|
||||
if (!array_key_exists('type', $field)) {
|
||||
unset($fields[$key]);
|
||||
continue;
|
||||
}
|
||||
// transform array fields
|
||||
if (in_array($field['type'], array(self::DT_JSON, self::DT_SERIALIZED)))
|
||||
$field['type']=$schema::DT_TEXT;
|
||||
// defaults values
|
||||
if (!array_key_exists('nullable', $field))
|
||||
$field['nullable'] = true;
|
||||
unset($field);
|
||||
unset($fields[$key]);
|
||||
continue;
|
||||
}
|
||||
// skip virtual fields with no type
|
||||
if (!array_key_exists('type', $field)) {
|
||||
unset($fields[$key]);
|
||||
continue;
|
||||
}
|
||||
// transform array fields
|
||||
if (in_array($field['type'], array(self::DT_JSON, self::DT_SERIALIZED)))
|
||||
$field['type']=$schema::DT_TEXT;
|
||||
// defaults values
|
||||
if (!array_key_exists('nullable', $field))
|
||||
$field['nullable'] = true;
|
||||
unset($field);
|
||||
}
|
||||
if (!in_array($table, $schema->getTables())) {
|
||||
// create table
|
||||
$table = $schema->createTable($table);
|
||||
if (isset($df) && $df['charset'])
|
||||
$table->setCharset($df['charset']);
|
||||
foreach ($fields as $field_key => $field_conf)
|
||||
$table->addColumn($field_key, $field_conf);
|
||||
if(isset($df) && $df['primary'] != 'id') {
|
||||
@@ -444,7 +482,8 @@ class Cortex extends Cursor {
|
||||
$mmTable = array($ftable.'__'.$fkey, $ptable.'__'.$pkey);
|
||||
natcasesort($mmTable);
|
||||
// shortcut for self-referencing mm tables
|
||||
if ($mmTable[0] == $mmTable[1] || ($fConf && $fConf['isSelf']==true))
|
||||
if ($mmTable[0] == $mmTable[1] ||
|
||||
($fConf && isset($fConf['isSelf']) && $fConf['isSelf']==true))
|
||||
return array_shift($mmTable);
|
||||
$return = strtolower(str_replace('\\', '_', implode('_mm_', $mmTable)));
|
||||
return $return;
|
||||
@@ -517,8 +556,8 @@ class Cortex extends Cursor {
|
||||
// has-many <> belongs-to-one (m:1)
|
||||
$field['has-many']['hasRel'] = 'belongs-to-one';
|
||||
$toConf=$rel['fieldConf'][$relConf[1]]['belongs-to-one'];
|
||||
if (is_array($toConf))
|
||||
$field['has-many']['relField'] = $toConf[1];
|
||||
$field['has-many']['relField'] = is_array($toConf) ?
|
||||
$toConf[1] : $rel['primary'];
|
||||
}
|
||||
} elseif(array_key_exists('has-one', $field))
|
||||
$field['relType'] = 'has-one';
|
||||
@@ -548,13 +587,12 @@ class Cortex extends Cursor {
|
||||
public function find($filter = NULL, array $options = NULL, $ttl = 0) {
|
||||
$sort=false;
|
||||
if ($this->dbsType!='sql') {
|
||||
if (!empty($this->countFields))
|
||||
// see if reordering is needed
|
||||
foreach($this->countFields as $counter) {
|
||||
if ($options && isset($options['order']) &&
|
||||
preg_match('/count_'.$counter.'\h+(asc|desc)/i',$options['order'],$match))
|
||||
$sort=true;
|
||||
}
|
||||
// see if reordering is needed
|
||||
foreach($this->countFields?:[] as $counter) {
|
||||
if ($options && isset($options['order']) &&
|
||||
preg_match('/count_'.$counter.'\h+(asc|desc)/i',$options['order'],$match))
|
||||
$sort=true;
|
||||
}
|
||||
if ($sort) {
|
||||
// backup slice settings
|
||||
if (isset($options['limit'])) {
|
||||
@@ -575,14 +613,13 @@ class Cortex extends Cursor {
|
||||
$record = $this->factory($record);
|
||||
unset($record);
|
||||
}
|
||||
if (!empty($this->countFields))
|
||||
// add counter for NoSQL engines
|
||||
foreach($this->countFields as $counter)
|
||||
foreach($result as &$mapper) {
|
||||
$cr=$mapper->get($counter);
|
||||
$mapper->virtual('count_'.$counter,$cr?count($cr):null);
|
||||
unset($mapper);
|
||||
}
|
||||
// add counter for NoSQL engines
|
||||
foreach($this->countFields?:[] as $counter)
|
||||
foreach($result as &$mapper) {
|
||||
$cr=$mapper->get($counter);
|
||||
$mapper->virtual('count_'.$counter,$cr?count($cr):null);
|
||||
unset($mapper);
|
||||
}
|
||||
$cc = new CortexCollection();
|
||||
$cc->setModels($result);
|
||||
if($sort) {
|
||||
@@ -599,7 +636,7 @@ class Cortex extends Cursor {
|
||||
* @param array $options
|
||||
* @param int $ttl
|
||||
* @param bool $count
|
||||
* @return array|false array of underlying cursor objects
|
||||
* @return array|int|false array of underlying cursor objects
|
||||
*/
|
||||
protected function filteredFind($filter = NULL, array $options = NULL, $ttl = 0, $count=false) {
|
||||
if ($this->grp_stack) {
|
||||
@@ -704,7 +741,7 @@ class Cortex extends Cursor {
|
||||
$filter[0] .= ' and ';
|
||||
$cond = array_shift($addToFilter);
|
||||
if ($this->dbsType=='sql')
|
||||
$cond = $this->_sql_prependTableToFields($cond,$this->table);
|
||||
$cond = $this->queryParser->sql_prependTableToFields($cond,$this->table);
|
||||
$filter[0] .= '('.$cond.')';
|
||||
$filter = array_merge($filter, $addToFilter);
|
||||
}
|
||||
@@ -717,31 +754,39 @@ class Cortex extends Cursor {
|
||||
if (isset($options['order']) && $this->db->driver() == 'pgsql')
|
||||
// PostgreSQLism: sort NULL values to the end of a table
|
||||
$options['order'] = preg_replace('/\h+DESC/i',' DESC NULLS LAST',$options['order']);
|
||||
if (!empty($hasJoin)) {
|
||||
if ($hasJoin) {
|
||||
// assemble full sql query
|
||||
$adhoc='';
|
||||
if ($count)
|
||||
$sql = 'SELECT COUNT(*) AS '.$this->db->quotekey('rows').' FROM '.$qtable;
|
||||
else {
|
||||
if (!empty($this->preBinds)) {
|
||||
if ($this->preBinds) {
|
||||
$crit = array_shift($filter);
|
||||
$filter = array_merge($this->preBinds,$filter);
|
||||
array_unshift($filter,$crit);
|
||||
}
|
||||
if (!empty($m_refl_adhoc))
|
||||
foreach ($m_refl_adhoc as $key=>$val)
|
||||
$adhoc.=', '.$val['expr'].' AS '.$key;
|
||||
$adhoc.=', '.$val['expr'].' AS '.$this->db->quotekey($key);
|
||||
$sql = 'SELECT '.$qtable.'.*'.$adhoc.' FROM '.$qtable;
|
||||
}
|
||||
$sql .= ' '.implode(' ',$hasJoin).' WHERE '.$filter[0];
|
||||
if (!$count) {
|
||||
$db=$this->db;
|
||||
if (isset($options['group']))
|
||||
$sql.=' GROUP BY '.preg_replace_callback('/\w+[._\-\w]*/i', function($match) use($db) {
|
||||
return $db->quotekey($match[0]);
|
||||
}, $options['group']);
|
||||
$sql.=' GROUP BY '.preg_replace_callback('/\w+[._\-\w]*/i',
|
||||
function($match) use($db) {
|
||||
return $db->quotekey($match[0]);
|
||||
}, $options['group']);
|
||||
if (isset($options['order']))
|
||||
$sql .= ' ORDER BY '.$options['order'];
|
||||
$sql.=' ORDER BY '.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'])));
|
||||
if (preg_match('/mssql|sqlsrv|odbc/', $this->db->driver()) &&
|
||||
(isset($options['limit']) || isset($options['offset']))) {
|
||||
$ofs=isset($options['offset'])?(int)$options['offset']:0;
|
||||
@@ -781,7 +826,7 @@ class Cortex extends Cursor {
|
||||
unset($record, $mapper);
|
||||
}
|
||||
return $result;
|
||||
} elseif (!empty($this->preBinds) && !$count) {
|
||||
} elseif (!empty($this->preBinds)) {
|
||||
// bind values to adhoc queries
|
||||
if (!$filter)
|
||||
// we (PDO) need any filter to bind values
|
||||
@@ -792,7 +837,7 @@ class Cortex extends Cursor {
|
||||
}
|
||||
}
|
||||
return ($count)
|
||||
? $this->mapper->count($filter, [], $ttl)
|
||||
? $this->mapper->count($filter,$options,$ttl)
|
||||
: $this->mapper->find($filter,$this->queryParser->prepareOptions($options,$this->dbsType),$ttl);
|
||||
}
|
||||
|
||||
@@ -932,8 +977,9 @@ class Cortex extends Cursor {
|
||||
$rel = $this->getRelInstance($relModel,null,$key);
|
||||
$fkey = is_array($this->fieldConf[$key]['belongs-to-one']) ?
|
||||
$this->fieldConf[$key]['belongs-to-one'][1] : $rel->primary;
|
||||
$query = $this->_sql_left_join($key,$this->table,$fkey,$table);
|
||||
$this->_sql_mergeRelCondition($cond,$table,$filter,$options);
|
||||
$alias = $table.'__'.$key;
|
||||
$query = $this->_sql_left_join($key,$this->table,$fkey,[$table,$alias]);
|
||||
$this->_sql_mergeRelCondition($cond,$alias,$filter,$options);
|
||||
return $query;
|
||||
}
|
||||
|
||||
@@ -970,11 +1016,11 @@ class Cortex extends Cursor {
|
||||
protected function _sql_mergeRelCondition($cond, $table, &$filter, &$options, $glue='AND') {
|
||||
if (!empty($cond[0])) {
|
||||
$whereClause = '('.array_shift($cond[0]).')';
|
||||
$whereClause = $this->_sql_prependTableToFields($whereClause,$table);
|
||||
$whereClause = $this->queryParser->sql_prependTableToFields($whereClause,$table);
|
||||
if (!$filter)
|
||||
$filter = array($whereClause);
|
||||
elseif (!empty($filter[0]))
|
||||
$filter[0] = '('.$this->_sql_prependTableToFields($filter[0],$this->table)
|
||||
$filter[0] = '('.$this->queryParser->sql_prependTableToFields($filter[0],$this->table)
|
||||
.') '.$glue.' '.$whereClause;
|
||||
$filter = array_merge($filter, $cond[0]);
|
||||
}
|
||||
@@ -984,24 +1030,6 @@ class Cortex extends Cursor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add table prefix to identifiers which do not have a table prefix yet
|
||||
* @param string $cond
|
||||
* @param string $table
|
||||
* @return string
|
||||
*/
|
||||
protected function _sql_prependTableToFields($cond, $table) {
|
||||
return preg_replace_callback('/(\w+\((?:[^)(]+|(?R))*\))|'.
|
||||
'(?:(\s)|^|(?<=[(]))([a-zA-Z_](?:[\w\-_]+))(?=[\s<>=!)]|$)/i',
|
||||
function($match) use($table) {
|
||||
if (!isset($match[3]))
|
||||
return $match[1];
|
||||
if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[3]))
|
||||
return $match[0];
|
||||
return $match[2].$table.'.'.$match[3];
|
||||
}, $cond);
|
||||
}
|
||||
|
||||
/**
|
||||
* add filter for loading related models
|
||||
* @param string $key
|
||||
@@ -1165,14 +1193,15 @@ class Cortex extends Cursor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Count records that match criteria
|
||||
* @param null $filter
|
||||
* @param array|NULL $options
|
||||
* @param array $options
|
||||
* @param int $ttl
|
||||
* @return array|false
|
||||
* @return mixed
|
||||
*/
|
||||
public function count($filter = NULL, array $options=NULL, $ttl = 60) {
|
||||
public function count($filter=NULL, array $options=NULL, $ttl=60) {
|
||||
$has=$this->hasCond;
|
||||
$count=$this->filteredFind($filter,null,$ttl,true);
|
||||
$count=$this->filteredFind($filter,$options,$ttl,true);
|
||||
$this->hasCond=$has;
|
||||
return $count;
|
||||
}
|
||||
@@ -1189,18 +1218,27 @@ class Cortex extends Cursor {
|
||||
* add a virtual field that counts occurring relations
|
||||
* @param $key
|
||||
*/
|
||||
public function countRel($key) {
|
||||
public function countRel($key, $alias=null, $filter=null, $option=null) {
|
||||
if (!$alias)
|
||||
$alias = 'count_'.$key;
|
||||
$filter_bak = null;
|
||||
if ($filter || $option) {
|
||||
$filter_bak = isset($this->relFilter[$key]) ? $this->relFilter[$key] : false;
|
||||
$this->filter($key,$filter,$option);
|
||||
}
|
||||
if (isset($this->fieldConf[$key])){
|
||||
// one-to-one, one-to-many
|
||||
if ($this->fieldConf[$key]['relType'] == 'belongs-to-one') {
|
||||
if ($this->dbsType == 'sql') {
|
||||
$this->mapper->set('count_'.$key,'count('.$key.')');
|
||||
$this->mapper->set($alias,'count('.$this->db->quotekey($key).')');
|
||||
$this->grp_stack=(!$this->grp_stack)?$key:$this->grp_stack.','.$key;
|
||||
if ($this->whitelist && !in_array($alias,$this->whitelist))
|
||||
$this->whitelist[] = $alias;
|
||||
} elseif ($this->dbsType == 'mongo')
|
||||
$this->_mongo_addGroup(array(
|
||||
'keys'=>array($key=>1),
|
||||
'reduce' => 'prev.count_'.$key.'++;',
|
||||
"initial" => array("count_".$key => 0)
|
||||
'reduce' => 'prev.'.$alias.'++;',
|
||||
"initial" => array($alias => 0)
|
||||
));
|
||||
else
|
||||
trigger_error('Cannot add direct relational counter.',E_USER_ERROR);
|
||||
@@ -1216,16 +1254,23 @@ class Cortex extends Cursor {
|
||||
if (array_key_exists($key, $this->relFilter) &&
|
||||
!empty($this->relFilter[$key][0])) {
|
||||
$options=array();
|
||||
$from = $mmTable.' '.$this->_sql_left_join($key,$mmTable,$relConf['relPK'],$relConf['relTable']);
|
||||
$from = $mmTable.' '.$this->_sql_left_join($key,$mmTable,
|
||||
$relConf['relPK'],$relConf['relTable']);
|
||||
$relFilter = $this->relFilter[$key];
|
||||
$this->_sql_mergeRelCondition($relFilter,$relConf['relTable'],$filter,$options);
|
||||
$this->_sql_mergeRelCondition($relFilter,$relConf['relTable'],
|
||||
$filter,$options);
|
||||
}
|
||||
$filter = $this->queryParser->prepareFilter($filter, $this->dbsType, $this->db, $this->fieldConf);
|
||||
$filter = $this->queryParser->prepareFilter($filter,
|
||||
$this->dbsType, $this->db, $this->fieldConf);
|
||||
$crit = array_shift($filter);
|
||||
if (count($filter)>0)
|
||||
$this->preBinds+=$filter;
|
||||
$this->mapper->set('count_'.$key,'(select count('.$mmTable.'.'.$relConf['relField'].') from '.$from.
|
||||
' where '.$crit.' group by '.$mmTable.'.'.$relConf['relField'].')');
|
||||
$this->preBinds=array_merge($this->preBinds,$filter);
|
||||
$this->mapper->set($alias,
|
||||
'(select count('.$this->db->quotekey($mmTable.'.'.$relConf['relField']).') from '.
|
||||
$this->db->quotekey($from).' where '.$crit.
|
||||
' group by '.$this->db->quotekey($mmTable.'.'.$relConf['relField']).')');
|
||||
if ($this->whitelist && !in_array($alias,$this->whitelist))
|
||||
$this->whitelist[] = $alias;
|
||||
} else {
|
||||
// count rel
|
||||
$this->countFields[]=$key;
|
||||
@@ -1235,15 +1280,22 @@ class Cortex extends Cursor {
|
||||
if ($this->dbsType == 'sql') {
|
||||
$fConf=$relConf[0]::resolveConfiguration();
|
||||
$fTable=$fConf['table'];
|
||||
$fAlias=$fTable.'__count';
|
||||
$rKey=$relConf[1];
|
||||
$crit = $fTable.'.'.$rKey.' = '.$this->table.'.'.$this->primary;
|
||||
$crit = $fAlias.'.'.$rKey.' = '.$this->table.'.'.$relConf['relField'];
|
||||
$filter = $this->mergeWithRelFilter($key,array($crit));
|
||||
$filter = $this->queryParser->prepareFilter($filter, $this->dbsType, $this->db, $this->fieldConf);
|
||||
$filter[0] = $this->queryParser->sql_prependTableToFields($filter[0],$fAlias);
|
||||
$filter = $this->queryParser->prepareFilter($filter,
|
||||
$this->dbsType, $this->db, $this->fieldConf);
|
||||
$crit = array_shift($filter);
|
||||
if (count($filter)>0)
|
||||
$this->preBinds+=$filter;
|
||||
$this->mapper->set('count_'.$key,'(select count('.$fTable.'.'.$fConf['primary'].') from '.$fTable.' where '.
|
||||
$crit.' group by '.$fTable.'.'.$rKey.')');
|
||||
$this->preBinds=array_merge($this->preBinds,$filter);
|
||||
$this->mapper->set($alias,
|
||||
'(select count('.$this->db->quotekey($fAlias.'.'.$fConf['primary']).') from '.
|
||||
$this->db->quotekey($fTable).' AS '.$this->db->quotekey($fAlias).' where '.
|
||||
$crit.' group by '.$this->db->quotekey($fAlias.'.'.$rKey).')');
|
||||
if ($this->whitelist && !in_array($alias,$this->whitelist))
|
||||
$this->whitelist[] = $alias;
|
||||
} else {
|
||||
// count rel
|
||||
$this->countFields[]=$key;
|
||||
@@ -1251,6 +1303,12 @@ class Cortex extends Cursor {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($filter_bak!==null) {
|
||||
if ($filter_bak)
|
||||
$this->relFilter[$key] = $filter_bak;
|
||||
else
|
||||
$this->clearFilter($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1405,12 +1463,9 @@ class Cortex extends Cursor {
|
||||
// update mapper fields
|
||||
$newField = $table->getCols(true);
|
||||
$newField = $newField[$key];
|
||||
$refl = new \ReflectionObject($this->mapper);
|
||||
$prop = $refl->getProperty('fields');
|
||||
$prop->setAccessible(true);
|
||||
$fields = $prop->getValue($this->mapper);
|
||||
$fields = $this->mapper->schema();
|
||||
$fields[$key] = $newField + array('value'=>NULL,'initial'=>NULL,'changed'=>NULL);
|
||||
$prop->setValue($this->mapper,$fields);
|
||||
$this->mapper->schema($fields);
|
||||
}
|
||||
}
|
||||
// custom setter
|
||||
@@ -1465,6 +1520,10 @@ class Cortex extends Cursor {
|
||||
*/
|
||||
public function virtual($key, $val) {
|
||||
$this->vFields[$key]=$val;
|
||||
if (!empty($this->whitelist)) {
|
||||
$this->whitelist[] = $key;
|
||||
$this->whitelist = array_unique($this->whitelist);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1730,6 +1789,15 @@ class Cortex extends Cursor {
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* return raw value of a field
|
||||
* @param $key
|
||||
* @return mixed
|
||||
*/
|
||||
function &getRaw($key) {
|
||||
return $this->get($key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* find the ID values of given relation collection
|
||||
* @param $val string|array|object|bool
|
||||
@@ -1741,7 +1809,7 @@ class Cortex extends Cursor {
|
||||
if (is_null($val))
|
||||
return NULL;
|
||||
if (is_object($val) && $val instanceof CortexCollection)
|
||||
$val = $val->expose();
|
||||
$val = $val->getAll($rel_field,true);
|
||||
elseif (is_string($val))
|
||||
// split-able string of collection IDs
|
||||
$val = \Base::instance()->split($val);
|
||||
@@ -1863,9 +1931,9 @@ class Cortex extends Cursor {
|
||||
$rel_depths = array('*'=>$rel_depths-1);
|
||||
elseif (is_array($rel_depths))
|
||||
$rel_depths['*'] = isset($rel_depths['*'])?--$rel_depths['*']:-1;
|
||||
if (!empty($this->fieldConf)) {
|
||||
if ($this->fieldConf) {
|
||||
$fields += array_fill_keys(array_keys($this->fieldConf),NULL);
|
||||
if($this->whitelist)
|
||||
if ($this->whitelist)
|
||||
$fields = array_intersect_key($fields, array_flip($this->whitelist));
|
||||
$mp = $obj ? : $this;
|
||||
foreach ($fields as $key => &$val) {
|
||||
@@ -2052,6 +2120,18 @@ class Cortex extends Cursor {
|
||||
($relField && isset($this->fieldConf[$key]['relType']));
|
||||
}
|
||||
|
||||
/**
|
||||
* return TRUE if any/specified field value has changed
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function changed($key=null) {
|
||||
if (method_exists($this->mapper,'changed'))
|
||||
return $this->mapper->changed($key);
|
||||
else
|
||||
trigger_error('method does not exist on mapper',E_USER_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* clear any mapper field or relation
|
||||
* @param string $key
|
||||
@@ -2180,24 +2260,24 @@ class CortexQueryParser extends \Prefab {
|
||||
// enhanced IN handling
|
||||
if (is_int(strpos($part, '?'))) {
|
||||
$val = array_shift($args);
|
||||
if (is_int($pos = strpos($part, 'IN ?'))) {
|
||||
if (is_int($pos = strpos($part, ' IN ?'))) {
|
||||
if ($val instanceof CortexCollection)
|
||||
$val = $val->getAll('_id',TRUE);
|
||||
if (!is_array($val) || empty($val))
|
||||
trigger_error(self::E_INBINDVALUE,E_USER_ERROR);
|
||||
$bindMarks = str_repeat('?,', count($val) - 1).'?';
|
||||
$part = substr($part, 0, $pos).'IN ('.$bindMarks.')';
|
||||
$bindMarks = str_repeat('?,',count($val) - 1).'?';
|
||||
$part = substr($part, 0, $pos).' IN ('.$bindMarks.') ';
|
||||
$ncond = array_merge($ncond, $val);
|
||||
} elseif($val === null && preg_match('/(\w+)\s*([!=<>]+)\s*\?/i',$part,$match)) {
|
||||
$part = $match[1].' IS '.($match[2]=='='||$match[2]=='=='?'':'NOT ').'NULL';
|
||||
} elseif($val === null &&
|
||||
preg_match('/((?:\S[\w\-]+\S.?)+)\s*'.
|
||||
'(!?==?)\s*(?:\?|:\w+)/i',$part,$match)) {
|
||||
$part = ' '.$match[1].' IS '.($match[2][0]=='!'?'NOT ':'').'NULL ';
|
||||
} else
|
||||
$ncond[] = $val;
|
||||
}
|
||||
unset($part);
|
||||
}
|
||||
array_unshift($ncond, implode($parts));
|
||||
// array_unshift($ncond, array_reduce($parts,function($out,$part){
|
||||
// return $out.((!$out||in_array($part,array('(',')'))
|
||||
// ||preg_match('/\($/',$out))?'':' ').$part;
|
||||
// },''));
|
||||
break;
|
||||
default:
|
||||
trigger_error(self::E_ENGINEERROR,E_USER_ERROR);
|
||||
@@ -2233,7 +2313,7 @@ class CortexQueryParser extends \Prefab {
|
||||
$pos = 0;
|
||||
foreach ($parts as &$part) {
|
||||
if (preg_match('/:\w+/i', $part, $match)) {
|
||||
if (!isset($args[$match[0]]))
|
||||
if (!array_key_exists($match[0],$args))
|
||||
trigger_error(sprintf(self::E_MISSINGBINDKEY,
|
||||
$match[0]),E_USER_ERROR);
|
||||
$part = str_replace($match[0], '?', $part);
|
||||
@@ -2249,10 +2329,9 @@ class CortexQueryParser extends \Prefab {
|
||||
* quote identifiers in condition
|
||||
* @param string $cond
|
||||
* @param object $db
|
||||
* @param string|bool $table
|
||||
* @return string
|
||||
*/
|
||||
public function sql_quoteCondition($cond, $db, $table=false) {
|
||||
public function sql_quoteCondition($cond, $db) {
|
||||
// https://www.debuggex.com/r/6AXwJ1Y3Aac8aocQ/3
|
||||
// https://regex101.com/r/yM5vK4/1
|
||||
// this took me lots of sleepless nights
|
||||
@@ -2261,12 +2340,33 @@ class CortexQueryParser extends \Prefab {
|
||||
'(?:(\b(?<!:)'. // exclude bind parameter ":foo"
|
||||
'[a-zA-Z_](?:[\w\-_.]+\.?))'. // match only identifier, exclude values
|
||||
'(?=[\s<>=!)]|$))/i', // only when part of condition or within brackets
|
||||
function($match) use($table,$db) {
|
||||
function($match) use($db) {
|
||||
if (!isset($match[2]))
|
||||
return $match[1];
|
||||
if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[2]))
|
||||
return $match[2];
|
||||
return $db->quotekey(($table?$table.'.':'').$match[2]);
|
||||
return $db->quotekey($match[2]);
|
||||
}, $cond);
|
||||
}
|
||||
|
||||
/**
|
||||
* add table prefix to identifiers which do not have a table prefix yet
|
||||
* @param string $cond
|
||||
* @param string $table
|
||||
* @return string
|
||||
*/
|
||||
public function sql_prependTableToFields($cond, $table) {
|
||||
return preg_replace_callback('/'.
|
||||
'(\w+\((?:[^)(]+|(?R))*\))|'.
|
||||
'(?:(\s)|^|(?<=[(]))'.
|
||||
'([a-zA-Z_](?:[\w\-_]+))'.
|
||||
'(?=[\s<>=!)]|$)/i',
|
||||
function($match) use($table) {
|
||||
if (!isset($match[3]))
|
||||
return $match[1];
|
||||
if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[3]))
|
||||
return $match[0];
|
||||
return $match[2].$table.'.'.$match[3];
|
||||
}, $cond);
|
||||
}
|
||||
|
||||
@@ -2299,6 +2399,8 @@ class CortexQueryParser extends \Prefab {
|
||||
$part = ($not ? '!' : '').'preg_match(?,'.$match[0].')';
|
||||
} // find IN operator
|
||||
elseif (is_int($pos = strpos($upart, ' @IN '))) {
|
||||
if ($val instanceof CortexCollection)
|
||||
$val = $val->getAll('_id',TRUE);
|
||||
if ($not = is_int($npos = strpos($upart, '@NOT')))
|
||||
$pos = $npos;
|
||||
$part = ($not ? '!' : '').'in_array('.substr($part, 0, $pos).
|
||||
@@ -2420,6 +2522,8 @@ class CortexQueryParser extends \Prefab {
|
||||
$var = array('$not' => $var);
|
||||
} // find IN operator
|
||||
elseif (in_array($upart, array('IN','NOT IN'))) {
|
||||
if ($var instanceof CortexCollection)
|
||||
$var = $var->getAll('_id',true);
|
||||
$var = array(($upart=='NOT IN')?'$nin':'$in' => array_values($var));
|
||||
} // translate operators
|
||||
elseif (!in_array($match[0], array('==', '='))) {
|
||||
@@ -2686,6 +2790,44 @@ class CortexCollection extends \ArrayIterator {
|
||||
unset($this[$ii]);
|
||||
}
|
||||
|
||||
/**
|
||||
* compare collection with a given ID stack
|
||||
* @param array|CortexCollection $stack
|
||||
* @param string $cpm_key
|
||||
* @return array
|
||||
*/
|
||||
public function compare($stack,$cpm_key='_id') {
|
||||
if ($stack instanceof CortexCollection)
|
||||
$stack = $stack->getAll($cpm_key,true);
|
||||
$keys = $this->getAll($cpm_key,true);
|
||||
$out = [];
|
||||
$new = array_diff($stack,$keys);
|
||||
$old = array_diff($keys,$stack);
|
||||
if ($new)
|
||||
$out['new'] = $new;
|
||||
if ($old)
|
||||
$out['old'] = $old;
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the collection contains a record with the given key-val set
|
||||
* @param mixed $val
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function contains($val,$key='_id') {
|
||||
$rel_ids = $this->getAll($key, true);
|
||||
if ($val instanceof Cursor)
|
||||
$val = $val->{$key};
|
||||
return in_array($val,$rel_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new hydrated collection from the given records
|
||||
* @param $records
|
||||
* @return CortexCollection
|
||||
*/
|
||||
static public function factory($records) {
|
||||
$cc = new self();
|
||||
$cc->setModels($records);
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
* | | < | <| -__|-- __|
|
||||
* |__|__|__||__|__|_____|_____|
|
||||
*
|
||||
* Copyright (c) 2015 by ikkez
|
||||
* Copyright (c) 2016 by ikkez
|
||||
* Christian Knuth <ikkez0n3@gmail.com>
|
||||
* https://github.com/ikkez/F3-Sugar/
|
||||
*
|
||||
* @package DB
|
||||
* @version 2.2.0-dev
|
||||
* @version 2.2.1
|
||||
* @date 25.04.2017
|
||||
**/
|
||||
|
||||
|
||||
@@ -58,9 +59,10 @@ class Schema extends DB_Utils {
|
||||
'mssql|sybase|dblib|odbc|sqlsrv' => 'float',
|
||||
'imb' => 'decfloat'
|
||||
),
|
||||
'DOUBLE' => array('mysql|sqlite2?|ibm' => 'DOUBLE',
|
||||
'pgsql' => 'double precision',
|
||||
'mssql|dblib|sybase|odbc|sqlsrv' => 'decimal',
|
||||
'DOUBLE' => array('mysql|ibm' => 'decimal(18,6)',
|
||||
'sqlite2?' => 'decimal(15,6)', // max 15-digit on sqlite
|
||||
'pgsql' => 'numeric(18,6)',
|
||||
'mssql|dblib|sybase|odbc|sqlsrv' => 'decimal(18,6)',
|
||||
),
|
||||
'VARCHAR128' => array('mysql|sqlite2?|ibm|mssql|sybase|dblib|odbc|sqlsrv' => 'varchar(128)',
|
||||
'pgsql' => 'character varying(128)',
|
||||
@@ -281,7 +283,7 @@ class Schema extends DB_Utils {
|
||||
'TRUNCATE TABLE '.$this->db->quotekey($name).';',
|
||||
'sqlite2?' => array(
|
||||
'DELETE FROM '.$this->db->quotekey($name).';',
|
||||
'UPDATE SQLITE_SEQUENCE SET seq = 0 WHERE name = '.$this->db->quotekey($name).';',
|
||||
// 'UPDATE SQLITE_SEQUENCE SET seq = 0 WHERE name = '.$this->db->quotekey($name).';',
|
||||
),
|
||||
);
|
||||
$query = $this->findQuery($cmd);
|
||||
@@ -348,7 +350,7 @@ abstract class TableBuilder extends DB_Utils {
|
||||
$key = $key->name;
|
||||
}
|
||||
if (array_key_exists($key,$this->columns))
|
||||
trigger_error(sprintf(self::TEXT_ColumnExists,$key));
|
||||
trigger_error(sprintf(self::TEXT_ColumnExists,$key),E_USER_ERROR);
|
||||
$column = new Column($key, $this);
|
||||
if ($args)
|
||||
foreach ($args as $arg => $val)
|
||||
@@ -450,6 +452,12 @@ class TableCreator extends TableBuilder {
|
||||
const
|
||||
TEXT_TableAlreadyExists = "Table `%s` already exists. Cannot create it.";
|
||||
|
||||
protected $charset='utf8';
|
||||
|
||||
public function setCharset($str) {
|
||||
$this->charset=$str;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate SQL query for creating a basic table, containing an ID serial field
|
||||
* and execute it if $exec is true, otherwise just return the generated query string
|
||||
@@ -460,7 +468,7 @@ class TableCreator extends TableBuilder {
|
||||
{
|
||||
// check if already existing
|
||||
if ($exec && in_array($this->name, $this->schema->getTables())) {
|
||||
trigger_error(sprintf(self::TEXT_TableAlreadyExists,$this->name));
|
||||
trigger_error(sprintf(self::TEXT_TableAlreadyExists,$this->name),E_USER_ERROR);
|
||||
return false;
|
||||
}
|
||||
$cols = '';
|
||||
@@ -468,7 +476,7 @@ class TableCreator extends TableBuilder {
|
||||
foreach ($this->columns as $cname => $column) {
|
||||
// no defaults for TEXT type
|
||||
if ($column->default !== false && is_int(strpos(strtoupper($column->type),'TEXT'))) {
|
||||
trigger_error(sprintf(self::TEXT_NoDefaultForTEXT, $column->name));
|
||||
trigger_error(sprintf(self::TEXT_NoDefaultForTEXT, $column->name),E_USER_ERROR);
|
||||
return false;
|
||||
}
|
||||
$cols .= ', '.$column->getColumnQuery();
|
||||
@@ -479,7 +487,7 @@ class TableCreator extends TableBuilder {
|
||||
'sqlite2?|sybase|dblib' =>
|
||||
"CREATE TABLE $table ($id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT".$cols.");",
|
||||
'mysql' =>
|
||||
"CREATE TABLE $table ($id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT".$cols.") DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;",
|
||||
"CREATE TABLE $table ($id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT".$cols.") DEFAULT CHARSET=$this->charset COLLATE ".$this->charset."_unicode_ci;",
|
||||
'pgsql' =>
|
||||
"CREATE TABLE $table ($id SERIAL PRIMARY KEY".$cols.");",
|
||||
'mssql|odbc|sqlsrv' =>
|
||||
@@ -544,10 +552,9 @@ class TableModifier extends TableBuilder {
|
||||
*/
|
||||
public function build($exec = TRUE)
|
||||
{
|
||||
|
||||
// check if table exists
|
||||
if (!in_array($this->name, $this->schema->getTables()))
|
||||
trigger_error(sprintf(self::TEXT_TableNotExisting, $this->name));
|
||||
trigger_error(sprintf(self::TEXT_TableNotExisting, $this->name),E_USER_ERROR);
|
||||
|
||||
if ($sqlite = preg_match('/sqlite2?/', $this->db->driver())) {
|
||||
$sqlite_queries = array();
|
||||
@@ -560,12 +567,12 @@ class TableModifier extends TableBuilder {
|
||||
/** @var Column $column */
|
||||
// not nullable fields should have a default value, when altering a table
|
||||
if ($column->default === false && $column->nullable === false) {
|
||||
trigger_error(sprintf(self::TEXT_NotNullFieldNeedsDefault, $column->name));
|
||||
trigger_error(sprintf(self::TEXT_NotNullFieldNeedsDefault, $column->name),E_USER_ERROR);
|
||||
return false;
|
||||
}
|
||||
// no defaults for TEXT type
|
||||
if($column->default !== false && is_int(strpos(strtoupper($column->type),'TEXT'))) {
|
||||
trigger_error(sprintf(self::TEXT_NoDefaultForTEXT, $column->name));
|
||||
trigger_error(sprintf(self::TEXT_NoDefaultForTEXT, $column->name),E_USER_ERROR);
|
||||
return false;
|
||||
}
|
||||
$table = $this->db->quotekey($this->name);
|
||||
@@ -831,9 +838,9 @@ class TableModifier extends TableBuilder {
|
||||
$existing_columns = $this->getCols(true);
|
||||
// check if column is already existing
|
||||
if (!in_array($name, array_keys($existing_columns)))
|
||||
trigger_error('cannot rename column. it does not exist.');
|
||||
trigger_error('cannot rename column. it does not exist.',E_USER_ERROR);
|
||||
if (in_array($new_name, array_keys($existing_columns)))
|
||||
trigger_error('cannot rename column. new column already exist.');
|
||||
trigger_error('cannot rename column. new column already exist.',E_USER_ERROR);
|
||||
|
||||
if (preg_match('/sqlite2?/', $this->db->driver()))
|
||||
// SQlite does not support drop or rename column directly
|
||||
@@ -992,7 +999,7 @@ class TableModifier extends TableBuilder {
|
||||
foreach($result as $row)
|
||||
$indexes[$row['Key_name']] = array('unique' => !(bool)$row['Non_unique']);
|
||||
} else
|
||||
trigger_error(sprintf(self::TEXT_ENGINE_NOT_SUPPORTED, $this->db->driver()));
|
||||
trigger_error(sprintf(self::TEXT_ENGINE_NOT_SUPPORTED, $this->db->driver()),E_USER_ERROR);
|
||||
return $indexes;
|
||||
}
|
||||
|
||||
@@ -1193,7 +1200,7 @@ class Column extends DB_Utils {
|
||||
*/
|
||||
public function getTypeVal() {
|
||||
if (!$this->type)
|
||||
trigger_error(sprintf('Cannot build a column query for `%s`: no column type set',$this->name));
|
||||
trigger_error(sprintf('Cannot build a column query for `%s`: no column type set',$this->name),E_USER_ERROR);
|
||||
if ($this->passThrough)
|
||||
$this->type_val = $this->type;
|
||||
else {
|
||||
@@ -1201,7 +1208,7 @@ class Column extends DB_Utils {
|
||||
if (!$this->type_val) {
|
||||
if (Schema::$strict) {
|
||||
trigger_error(sprintf(self::TEXT_NoDataType, strtoupper($this->type),
|
||||
$this->db->driver()));
|
||||
$this->db->driver()),E_USER_ERROR);
|
||||
return FALSE;
|
||||
} else {
|
||||
// auto pass-through if not found
|
||||
@@ -1265,7 +1272,7 @@ class Column extends DB_Utils {
|
||||
if ($this->type != 'TIMESTAMP' &&
|
||||
($this->passThrough && strtoupper($this->type) != strtoupper($stamp_type))
|
||||
)
|
||||
trigger_error(self::TEXT_CurrentStampDataType);
|
||||
trigger_error(self::TEXT_CurrentStampDataType,E_USER_ERROR);
|
||||
return $this->findQuery($this->schema->defaultTypes[strtoupper($this->default)]);
|
||||
} else {
|
||||
// static defaults
|
||||
@@ -1301,7 +1308,7 @@ class DB_Utils {
|
||||
foreach ($cmd as $backend => $val)
|
||||
if (preg_match('/'.$backend.'/', $this->db->driver()))
|
||||
return $val;
|
||||
trigger_error(sprintf(self::TEXT_ENGINE_NOT_SUPPORTED, $this->db->driver()));
|
||||
trigger_error(sprintf(self::TEXT_ENGINE_NOT_SUPPORTED, $this->db->driver()),E_USER_ERROR);
|
||||
}
|
||||
|
||||
public function __construct(SQL $db) {
|
||||
|
||||
@@ -653,7 +653,6 @@ class CharacterModel extends BasicModel {
|
||||
if( !$characterLog = $this->getLog() ){
|
||||
// create new log
|
||||
$characterLog = $this->rel('characterLog');
|
||||
$characterLog->characterId = $this->_id;
|
||||
}
|
||||
|
||||
// get current log data and modify on change
|
||||
@@ -720,6 +719,7 @@ class CharacterModel extends BasicModel {
|
||||
}
|
||||
|
||||
$characterLog->setData($logData);
|
||||
$characterLog->characterId = $this;
|
||||
$characterLog->save();
|
||||
|
||||
$this->characterLog = $characterLog;
|
||||
@@ -745,6 +745,7 @@ class CharacterModel extends BasicModel {
|
||||
$deleteLog = true;
|
||||
}
|
||||
|
||||
//in case of failure (invalid API response) increase or reset "retry counter"
|
||||
if( $user = $this->getUser() ){
|
||||
// Session data does not exists in CLI mode (Cronjob)
|
||||
if( $sessionCharacterData = $user->getSessionCharacterData($this->id, false) ){
|
||||
@@ -840,7 +841,7 @@ class CharacterModel extends BasicModel {
|
||||
$this->hasLog() &&
|
||||
!$this->characterLog->dry()
|
||||
){
|
||||
$characterLog = &$this->characterLog;
|
||||
$characterLog = $this->characterLog;
|
||||
}
|
||||
|
||||
return $characterLog;
|
||||
|
||||
@@ -573,11 +573,9 @@ class MapModel extends BasicModel {
|
||||
|
||||
/**
|
||||
* get all (private) characters for this map
|
||||
* @param array $characterIds
|
||||
* @param array $options
|
||||
* @return CharacterModel[]
|
||||
*/
|
||||
private function getCharacters($characterIds = [], $options = []){
|
||||
private function getCharacters(){
|
||||
$characters = [];
|
||||
$filter = ['active = ?', 1];
|
||||
|
||||
@@ -588,11 +586,6 @@ class MapModel extends BasicModel {
|
||||
|
||||
$this->filter('mapCharacters', $filter);
|
||||
|
||||
if($options['hasLog']){
|
||||
// just characters with active log data
|
||||
$this->has('mapCharacters.characterLog', ['active = ?', 1]);
|
||||
}
|
||||
|
||||
if($this->mapCharacters){
|
||||
foreach($this->mapCharacters as $characterMapModel){
|
||||
$characters[] = $characterMapModel->characterId;
|
||||
@@ -611,7 +604,7 @@ class MapModel extends BasicModel {
|
||||
$characters = [];
|
||||
|
||||
if($this->isPrivate()){
|
||||
$activeCharacters = $this->getCharacters([], $options);
|
||||
$activeCharacters = $this->getCharacters();
|
||||
|
||||
// add active character for each user
|
||||
foreach($activeCharacters as $activeCharacter){
|
||||
|
||||
@@ -190,7 +190,6 @@ define([
|
||||
// =================================================================
|
||||
|
||||
// user count changed -> change tooltip content
|
||||
let tooltipOptions = {placement: 'top', trigger: 'manual'};
|
||||
|
||||
// set tooltip color
|
||||
let highlight = false;
|
||||
@@ -203,10 +202,12 @@ define([
|
||||
tooltipIconClass = 'fa-caret-down';
|
||||
}
|
||||
|
||||
tooltipOptions.id = systemId;
|
||||
tooltipOptions.highlight = highlight;
|
||||
tooltipOptions.title = '<i class="fa ' + tooltipIconClass + '"></i>';
|
||||
tooltipOptions.title += ' ' + userCounter;
|
||||
let tooltipOptions = {
|
||||
trigger: 'manual',
|
||||
id: systemId,
|
||||
highlight: highlight,
|
||||
title: '<i class="fa ' + tooltipIconClass + '"></i> ' + userCounter
|
||||
};
|
||||
|
||||
// show system head
|
||||
systemHeadExpand.velocity('stop', true).velocity({
|
||||
@@ -364,6 +365,7 @@ define([
|
||||
'<div id="' + tooltipId + '" class="tooltip-inner txt-color ' + config.systemTooltipInnerClass + '"></div>' +
|
||||
'</div>';
|
||||
|
||||
options.placement = getSystemTooltipPlacement(system);
|
||||
options.html = true;
|
||||
options.animation = true;
|
||||
options.template = template;
|
||||
@@ -405,6 +407,9 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
// update tooltip placement based on system position
|
||||
system.data('bs.tooltip').options.placement = getSystemTooltipPlacement(system);
|
||||
|
||||
// show() can be forced
|
||||
if(options.show === true){
|
||||
system.tooltip('show');
|
||||
@@ -412,11 +417,21 @@ define([
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* get tooltip position based on current system position
|
||||
* @param system
|
||||
* @returns {string}
|
||||
*/
|
||||
let getSystemTooltipPlacement = (system) => {
|
||||
let offsetParent = system.parent().offset();
|
||||
let offsetSystem = system.offset();
|
||||
|
||||
return (offsetSystem.top - offsetParent.top < 27) ? 'bottom' : 'top';
|
||||
};
|
||||
|
||||
/**
|
||||
* set or change the status of a system
|
||||
* @param status
|
||||
|
||||
@@ -190,7 +190,6 @@ define([
|
||||
// =================================================================
|
||||
|
||||
// user count changed -> change tooltip content
|
||||
let tooltipOptions = {placement: 'top', trigger: 'manual'};
|
||||
|
||||
// set tooltip color
|
||||
let highlight = false;
|
||||
@@ -203,10 +202,12 @@ define([
|
||||
tooltipIconClass = 'fa-caret-down';
|
||||
}
|
||||
|
||||
tooltipOptions.id = systemId;
|
||||
tooltipOptions.highlight = highlight;
|
||||
tooltipOptions.title = '<i class="fa ' + tooltipIconClass + '"></i>';
|
||||
tooltipOptions.title += ' ' + userCounter;
|
||||
let tooltipOptions = {
|
||||
trigger: 'manual',
|
||||
id: systemId,
|
||||
highlight: highlight,
|
||||
title: '<i class="fa ' + tooltipIconClass + '"></i> ' + userCounter
|
||||
};
|
||||
|
||||
// show system head
|
||||
systemHeadExpand.velocity('stop', true).velocity({
|
||||
@@ -364,6 +365,7 @@ define([
|
||||
'<div id="' + tooltipId + '" class="tooltip-inner txt-color ' + config.systemTooltipInnerClass + '"></div>' +
|
||||
'</div>';
|
||||
|
||||
options.placement = getSystemTooltipPlacement(system);
|
||||
options.html = true;
|
||||
options.animation = true;
|
||||
options.template = template;
|
||||
@@ -405,6 +407,9 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
// update tooltip placement based on system position
|
||||
system.data('bs.tooltip').options.placement = getSystemTooltipPlacement(system);
|
||||
|
||||
// show() can be forced
|
||||
if(options.show === true){
|
||||
system.tooltip('show');
|
||||
@@ -412,11 +417,21 @@ define([
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* get tooltip position based on current system position
|
||||
* @param system
|
||||
* @returns {string}
|
||||
*/
|
||||
let getSystemTooltipPlacement = (system) => {
|
||||
let offsetParent = system.parent().offset();
|
||||
let offsetSystem = system.offset();
|
||||
|
||||
return (offsetSystem.top - offsetParent.top < 27) ? 'bottom' : 'top';
|
||||
};
|
||||
|
||||
/**
|
||||
* set or change the status of a system
|
||||
* @param status
|
||||
|
||||
Reference in New Issue
Block a user