- New "Intel module" for Citadel tracking, closed #246
- added some new tables (for SDE replacement), #628 - new "index build" functions added to `/setup`, #628 - updated "Cortex" PHP ORM lib `1.5.0` -> `1.6.0-dev`
This commit is contained in:
@@ -16,6 +16,10 @@ HALT = FALSE
|
||||
; Timezone to use. Sync program with eve server time. (default: UTC)
|
||||
TZ = UTC
|
||||
|
||||
; Cache key prefix. Same for all cache values for this installation
|
||||
; CLI (cronjob) scripts use it for cache manipulation
|
||||
SEED = {{ md5(@SERVER.SERVER_NAME) }}
|
||||
|
||||
; Cache backend. Can handle Redis, Memcache module, APC, WinCache, XCache and a filesystem-based cache.
|
||||
; (default: folder=tmp/cache/)
|
||||
CACHE = folder=tmp/cache/
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
* https://github.com/ikkez/F3-Sugar/
|
||||
*
|
||||
* @package DB
|
||||
* @version 1.5.0
|
||||
* @date 30.06.2017
|
||||
* @version 1.6.0-dev
|
||||
* @date 25.04.2018
|
||||
* @since 24.04.2012
|
||||
*/
|
||||
|
||||
@@ -157,11 +157,19 @@ class Cortex extends Cursor {
|
||||
$f3->get('CORTEX.standardiseID') : TRUE;
|
||||
if(!empty($this->fieldConf))
|
||||
foreach($this->fieldConf as &$conf) {
|
||||
$conf=static::resolveRelationConf($conf);
|
||||
$conf=static::resolveRelationConf($conf,$this->primary);
|
||||
unset($conf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return raw mapper instance
|
||||
* @return Cursor
|
||||
*/
|
||||
public function getMapper() {
|
||||
return $this->mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* get fields or set whitelist / blacklist of fields
|
||||
* @param array $fields
|
||||
@@ -512,9 +520,10 @@ class Cortex extends Cursor {
|
||||
/**
|
||||
* resolve relation field types
|
||||
* @param array $field
|
||||
* @param string $pkey
|
||||
* @return array
|
||||
*/
|
||||
protected static function resolveRelationConf($field) {
|
||||
protected static function resolveRelationConf($field,$pkey=NULL) {
|
||||
if (array_key_exists('belongs-to-one', $field)) {
|
||||
// find primary field definition
|
||||
if (!is_array($relConf = $field['belongs-to-one']))
|
||||
@@ -552,7 +561,10 @@ class Cortex extends Cursor {
|
||||
$field['has-many']['relField'] = $relConf[1];
|
||||
$field['has-many']['relFieldType'] = isset($rel['fieldConf'][$relConf[1]]['type']) ?
|
||||
$rel['fieldConf'][$relConf[1]]['type'] : Schema::DT_INT;
|
||||
$field['has-many']['relPK'] = isset($relConf[3])?$relConf[3]:$rel['primary'];
|
||||
$field['has-many']['relPK'] = isset($relConf['relPK'])?
|
||||
$relConf['relPK']:$rel['primary'];
|
||||
$field['has-many']['localKey'] = isset($relConf['localKey'])?
|
||||
$relConf['localKey']:($pkey?:'_id');
|
||||
} else {
|
||||
// has-many <> belongs-to-one (m:1)
|
||||
$field['has-many']['hasRel'] = 'belongs-to-one';
|
||||
@@ -713,7 +725,15 @@ class Cortex extends Cursor {
|
||||
}
|
||||
elseif ($result = $this->_hasRefsInMM($key,$has_filter,$has_options,$ttl))
|
||||
$addToFilter = array($id.' IN ?', $result);
|
||||
} // *-to-one
|
||||
}
|
||||
// *-to-one
|
||||
elseif ($this->dbsType == 'sql') {
|
||||
// use sub-query inclusion
|
||||
$has_filter=$this->mergeFilter([$has_filter,
|
||||
[$this->rel($key)->getTable().'.'.$fromConf[1].'='.$this->getTable().'.'.$id]]);
|
||||
$result = $this->_refSubQuery($key,$has_filter,$has_options);
|
||||
$addToFilter = ['exists('.$result[0].')']+$result[1];
|
||||
}
|
||||
elseif ($result = $this->_hasRefsIn($key,$has_filter,$has_options,$ttl))
|
||||
$addToFilter = array($id.' IN ?', $result);
|
||||
break;
|
||||
@@ -754,7 +774,7 @@ class Cortex extends Cursor {
|
||||
$qtable = $this->db->quotekey($this->table);
|
||||
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']);
|
||||
$options['order'] = preg_replace('/\h+DESC(?=\s*(?:$|,))/i',' DESC NULLS LAST',$options['order']);
|
||||
if ($hasJoin) {
|
||||
// assemble full sql query
|
||||
$adhoc='';
|
||||
@@ -838,7 +858,7 @@ class Cortex extends Cursor {
|
||||
}
|
||||
}
|
||||
if ($options) {
|
||||
$options = $this->queryParser->prepareOptions($options,$this->dbsType);
|
||||
$options = $this->queryParser->prepareOptions($options,$this->dbsType,$this->db);
|
||||
if ($count)
|
||||
unset($options['order']);
|
||||
}
|
||||
@@ -912,6 +932,22 @@ class Cortex extends Cursor {
|
||||
return empty($hasSetByRelId) ? false : $hasSetByRelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* build sub query on relation
|
||||
* @param $key
|
||||
* @param $filter
|
||||
* @param $options
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _refSubQuery($key, $filter, $options,$fields=null) {
|
||||
$type = $this->fieldConf[$key]['relType'];
|
||||
$fieldConf = $this->fieldConf[$key][$type];
|
||||
$rel = $this->getRelFromConf($fieldConf,$key);
|
||||
$filter[0]=$this->queryParser->sql_quoteCondition($filter[0],$this->db);
|
||||
return $rel->mapper->stringify(implode(',',array_map([$this->db,'quotekey'],
|
||||
$fields?:[$rel->primary])),$filter,$options);
|
||||
}
|
||||
|
||||
/**
|
||||
* return IDs of own mappers that match the given relation filter on pivot tables
|
||||
* @param string $key
|
||||
@@ -1164,30 +1200,69 @@ class Cortex extends Cursor {
|
||||
foreach($this->saveCsd as $key => $val) {
|
||||
if($fields[$key]['relType'] == 'has-many') {
|
||||
$relConf = $fields[$key]['has-many'];
|
||||
$mmTable = $this->mmTable($relConf,$key);
|
||||
$rel = $this->getRelInstance(null, array('db'=>$this->db, 'table'=>$mmTable));
|
||||
$id = $this->get($relConf['relPK'],true);
|
||||
$filter = [$relConf['relField'].' = ?',$id];
|
||||
if ($relConf['isSelf']) {
|
||||
$filter[0].= ' OR '.$relConf['relField'].'_ref = ?';
|
||||
$filter[] = $id;
|
||||
if ($relConf['hasRel'] == 'has-many') {
|
||||
$mmTable = $this->mmTable($relConf,$key);
|
||||
$mm = $this->getRelInstance(null, array('db'=>$this->db, 'table'=>$mmTable));
|
||||
$id = $this->get($relConf['localKey'],true);
|
||||
$filter = [$relConf['relField'].' = ?',$id];
|
||||
if ($relConf['isSelf']) {
|
||||
$filter[0].= ' OR '.$relConf['relField'].'_ref = ?';
|
||||
$filter[] = $id;
|
||||
}
|
||||
// delete all refs
|
||||
if (is_null($val))
|
||||
$mm->erase($filter);
|
||||
// update refs
|
||||
elseif (is_array($val)) {
|
||||
$mm->erase($filter);
|
||||
foreach($val as $v) {
|
||||
if ($relConf['isSelf'] && $v==$id)
|
||||
continue;
|
||||
$mm->set($key,$v);
|
||||
$mm->set($relConf['relField'].($relConf['isSelf']?'_ref':''),$id);
|
||||
$mm->save();
|
||||
$mm->reset();
|
||||
}
|
||||
}
|
||||
unset($mm);
|
||||
}
|
||||
// delete all refs
|
||||
if (is_null($val))
|
||||
$rel->erase($filter);
|
||||
// update refs
|
||||
elseif (is_array($val)) {
|
||||
$rel->erase($filter);
|
||||
foreach($val as $v) {
|
||||
if ($relConf['isSelf'] && $v==$id)
|
||||
continue;
|
||||
$rel->set($key,$v);
|
||||
$rel->set($relConf['relField'].($relConf['isSelf']?'_ref':''),$id);
|
||||
$rel->save();
|
||||
$rel->reset();
|
||||
elseif($relConf['hasRel'] == 'belongs-to-one') {
|
||||
$rel = $this->getRelInstance($relConf[0],$relConf,$key);
|
||||
// find existing relations
|
||||
$refs = $rel->find([$relConf[1].' = ?',$this->getRaw($relConf['relField'])]);
|
||||
if (is_null($val)) {
|
||||
foreach ($refs?:[] as $model) {
|
||||
$model->set($relConf[1],NULL);
|
||||
$model->save();
|
||||
}
|
||||
$this->fieldsCache[$key] = NULL;
|
||||
} else {
|
||||
if ($refs) {
|
||||
$ref_ids = $refs->getAll('_id');
|
||||
// unlink removed relations
|
||||
$remove_refs = array_diff($ref_ids,$val);
|
||||
foreach ($refs as $model)
|
||||
if (in_array($model->getRaw($relConf['relField']),$remove_refs)) {
|
||||
$model->set($relConf[1],null);
|
||||
$model->save();
|
||||
}
|
||||
// get new relation keys
|
||||
$val = array_diff($val,$ref_ids);
|
||||
} else
|
||||
$refs = new CortexCollection();
|
||||
if (!empty($val)) {
|
||||
// find models that need to be linked
|
||||
$new_refs = $rel->find([$relConf['relField'].' IN ?',$val]);
|
||||
foreach ($new_refs?:[] as $model) {
|
||||
// set relation to new models
|
||||
$model->set($relConf[1],$this->getRaw($relConf['relField']));
|
||||
$model->save();
|
||||
$refs->add($model);
|
||||
}
|
||||
}
|
||||
$this->fieldsCache[$key] = $refs;
|
||||
}
|
||||
}
|
||||
unset($rel);
|
||||
} elseif($fields[$key]['relType'] == 'has-one') {
|
||||
$val->save();
|
||||
}
|
||||
@@ -1372,7 +1447,9 @@ class Cortex extends Cursor {
|
||||
if (is_null($val))
|
||||
$val = NULL;
|
||||
elseif (is_object($val) &&
|
||||
!($this->dbsType=='mongo' && $val instanceof \MongoId)) {
|
||||
!($this->dbsType=='mongo' && (
|
||||
($this->db->legacy() && $val instanceof \MongoId) ||
|
||||
(!$this->db->legacy() && $val instanceof \MongoDB\BSON\ObjectId)))) {
|
||||
// fetch fkey from mapper
|
||||
if (!$val instanceof Cortex || $val->dry())
|
||||
trigger_error(self::E_INVALID_RELATION_OBJECT,E_USER_ERROR);
|
||||
@@ -1381,8 +1458,9 @@ class Cortex extends Cursor {
|
||||
$rel_field = (is_array($relConf) ? $relConf[1] : '_id');
|
||||
$val = $val->get($rel_field,true);
|
||||
}
|
||||
} elseif ($this->dbsType == 'mongo' && !$val instanceof \MongoId)
|
||||
$val = new \MongoId($val);
|
||||
} elseif ($this->dbsType == 'mongo' && (($this->db->legacy() && !$val instanceof \MongoId)
|
||||
|| (!$this->db->legacy() && !$val instanceof \MongoDB\BSON\ObjectId)))
|
||||
$val = $this->db->legacy() ? new \MongoId($val) : new \MongoDB\BSON\ObjectId($val);
|
||||
} elseif (isset($fields[$key]['has-one'])){
|
||||
$relConf = $fields[$key]['has-one'];
|
||||
if (is_null($val)) {
|
||||
@@ -1406,45 +1484,54 @@ class Cortex extends Cursor {
|
||||
$val = $this->getForeignKeysArray($val, $rel_field, $key);
|
||||
}
|
||||
elseif (isset($fields[$key]['has-many'])) {
|
||||
// many-to-many, bidirectional
|
||||
$relConf = $fields[$key]['has-many'];
|
||||
if ($relConf['hasRel'] == 'has-many') {
|
||||
// many-to-many, bidirectional
|
||||
// many-to-one, inverse
|
||||
if ($relConf['hasRel'] == 'has-many'
|
||||
|| $relConf['hasRel'] == 'belongs-to-one') {
|
||||
// custom setter
|
||||
$val = $this->emit('set_'.$key, $val);
|
||||
$val = $this->getForeignKeysArray($val,'_id',$key);
|
||||
if (empty($val) && is_array($val))
|
||||
$val=new CortexCollection();
|
||||
$this->saveCsd[$key] = $val; // array of keys
|
||||
$this->fieldsCache[$key] = $val;
|
||||
return $val;
|
||||
} elseif ($relConf['hasRel'] == 'belongs-to-one') {
|
||||
// TODO: many-to-one, bidirectional, inverse way
|
||||
trigger_error("not implemented",E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
// convert array content
|
||||
if (is_array($val) && $this->dbsType == 'sql')
|
||||
if ($fields[$key]['type'] == self::DT_SERIALIZED)
|
||||
$val = serialize($val);
|
||||
elseif ($fields[$key]['type'] == self::DT_JSON)
|
||||
$val = json_encode($val);
|
||||
else
|
||||
trigger_error(sprintf(self::E_ARRAY_DATATYPE, $key),E_USER_ERROR);
|
||||
// add nullable polyfill
|
||||
if ($val === NULL && ($this->dbsType == 'jig' || $this->dbsType == 'mongo')
|
||||
&& array_key_exists('nullable', $fields[$key]) && $fields[$key]['nullable'] === false)
|
||||
trigger_error(sprintf(self::E_NULLABLE_COLLISION,$key),E_USER_ERROR);
|
||||
// MongoId shorthand
|
||||
if ($this->dbsType == 'mongo' && !$val instanceof \MongoId) {
|
||||
if ($this->dbsType == 'mongo' && (($this->db->legacy() && !$val instanceof \MongoId)
|
||||
|| (!$this->db->legacy() && !$val instanceof \MongoDB\BSON\ObjectId))) {
|
||||
if ($key == '_id')
|
||||
$val = new \MongoId($val);
|
||||
$val = $this->db->legacy() ? new \MongoId($val) : new \MongoDB\BSON\ObjectId($val);
|
||||
elseif (preg_match('/INT/i',$fields[$key]['type'])
|
||||
&& !isset($fields[$key]['relType']))
|
||||
$val = (int) $val;
|
||||
}
|
||||
// cast boolean
|
||||
if (preg_match('/BOOL/i',$fields[$key]['type'])) {
|
||||
$val = !$val || $val==='false' ? false : (bool) $val;
|
||||
if ($this->dbsType == 'sql')
|
||||
$val = (int) $val;
|
||||
}
|
||||
// custom setter
|
||||
$val = $this->emit('set_'.$key, $val);
|
||||
// convert array content
|
||||
if (is_array($val) && $this->dbsType == 'sql') {
|
||||
if ($fields[$key]['type']==self::DT_SERIALIZED)
|
||||
$val=serialize($val);
|
||||
elseif ($fields[$key]['type']==self::DT_JSON)
|
||||
$val=json_encode($val);
|
||||
else
|
||||
trigger_error(sprintf(self::E_ARRAY_DATATYPE,$key),E_USER_ERROR);
|
||||
}
|
||||
} else {
|
||||
// custom setter
|
||||
$val = $this->emit('set_'.$key, $val);
|
||||
}
|
||||
// fluid SQL
|
||||
if ($this->fluid && $this->dbsType == 'sql') {
|
||||
@@ -1474,8 +1561,6 @@ class Cortex extends Cursor {
|
||||
$this->mapper->schema($fields);
|
||||
}
|
||||
}
|
||||
// custom setter
|
||||
$val = $this->emit('set_'.$key, $val);
|
||||
return $this->mapper->set($key, $val);
|
||||
}
|
||||
|
||||
@@ -1555,6 +1640,8 @@ class Cortex extends Cursor {
|
||||
}
|
||||
if ($raw) {
|
||||
$out = $this->exists($key) ? $this->mapper->{$key} : NULL;
|
||||
if ($this->dbsType == 'mongo' && !$this->db->legacy() && $out instanceof \MongoDB\Model\BSONArray)
|
||||
$out = (array) $out;
|
||||
return $out;
|
||||
}
|
||||
if (!empty($fields) && isset($fields[$key]) && is_array($fields[$key])
|
||||
@@ -1692,7 +1779,7 @@ class Cortex extends Cursor {
|
||||
} // no collection
|
||||
else {
|
||||
// find foreign keys
|
||||
$fId=$this->get($fromConf['relPK'],true);
|
||||
$fId=$this->get($fromConf['localKey'],true);
|
||||
$filter = [$fromConf['relField'].' = ?',$fId];
|
||||
if ($fromConf['isSelf']) {
|
||||
$filter = [$fromConf['relField'].' = ?',$fId];
|
||||
@@ -1713,7 +1800,7 @@ class Cortex extends Cursor {
|
||||
unset($rel);
|
||||
$rel = $this->getRelInstance($fromConf[0],null,$key,true);
|
||||
// load foreign models
|
||||
$filter = array($toConf['relPK'].' IN ?', $fkeys);
|
||||
$filter = array($fromConf['relPK'].' IN ?', $fkeys);
|
||||
$filter = $this->mergeWithRelFilter($key, $filter);
|
||||
$this->fieldsCache[$key] = $rel->find($filter,
|
||||
$this->getRelFilterOption($key),$this->_ttl);
|
||||
@@ -1724,7 +1811,7 @@ class Cortex extends Cursor {
|
||||
elseif (isset($fields[$key]['belongs-to-many'])) {
|
||||
// many-to-many, unidirectional
|
||||
$fields[$key]['type'] = self::DT_JSON;
|
||||
$result = !$this->exists($key) ? null :$this->mapper->get($key);
|
||||
$result = $this->getRaw($key);
|
||||
if ($this->dbsType == 'sql')
|
||||
$result = json_decode($result, true);
|
||||
if (!is_array($result))
|
||||
@@ -1786,7 +1873,8 @@ class Cortex extends Cursor {
|
||||
// fetch cached value, if existing
|
||||
$val = array_key_exists($key,$this->fieldsCache) ? $this->fieldsCache[$key]
|
||||
: (($this->exists($key)) ? $this->mapper->{$key} : null);
|
||||
if ($this->dbsType == 'mongo' && $val instanceof \MongoId) {
|
||||
if ($this->dbsType == 'mongo' && (($this->db->legacy() && $val instanceof \MongoId) ||
|
||||
(!$this->db->legacy() && $val instanceof \MongoDB\BSON\ObjectId))) {
|
||||
// conversion to string makes further processing in template, etc. much easier
|
||||
$val = (string) $val;
|
||||
}
|
||||
@@ -1836,13 +1924,14 @@ class Cortex extends Cursor {
|
||||
$isMongo = ($this->dbsType == 'mongo');
|
||||
foreach ($val as &$item) {
|
||||
if (is_object($item) &&
|
||||
!($isMongo && $item instanceof \MongoId)) {
|
||||
!($isMongo && (($this->db->legacy() && $item instanceof \MongoId) ||
|
||||
(!$this->db->legacy() && $item instanceof \MongoDB\BSON\ObjectId)))) {
|
||||
if (!$item instanceof Cortex || $item->dry())
|
||||
trigger_error(self::E_INVALID_RELATION_OBJECT,E_USER_ERROR);
|
||||
else $item = $item->get($rel_field,true);
|
||||
}
|
||||
if ($isMongo && $rel_field == '_id' && is_string($item))
|
||||
$item = new \MongoId($item);
|
||||
$item = $this->db->legacy() ? new \MongoId($item) : new \MongoDB\BSON\ObjectId($item);
|
||||
if (is_numeric($item))
|
||||
$item = (int) $item;
|
||||
unset($item);
|
||||
@@ -1896,16 +1985,16 @@ class Cortex extends Cursor {
|
||||
|
||||
/**
|
||||
* get relation model from config
|
||||
* @param $fieldConf
|
||||
* @param $relConf
|
||||
* @param $key
|
||||
* @return Cortex
|
||||
*/
|
||||
protected function getRelFromConf(&$fieldConf, $key) {
|
||||
if (!is_array($fieldConf))
|
||||
$fieldConf = array($fieldConf, '_id');
|
||||
$rel = $this->getRelInstance($fieldConf[0],null,$key,true);
|
||||
if($this->dbsType=='sql' && $fieldConf[1] == '_id')
|
||||
$fieldConf[1] = $rel->primary;
|
||||
protected function getRelFromConf(&$relConf, $key) {
|
||||
if (!is_array($relConf))
|
||||
$relConf = array($relConf, '_id');
|
||||
$rel = $this->getRelInstance($relConf[0],null,$key,true);
|
||||
if($this->dbsType=='sql' && $relConf[1] == '_id')
|
||||
$relConf[1] = $rel->primary;
|
||||
return $rel;
|
||||
}
|
||||
|
||||
@@ -2097,10 +2186,11 @@ class Cortex extends Cursor {
|
||||
public function reset($mapper = true) {
|
||||
if ($mapper)
|
||||
$this->mapper->reset();
|
||||
$this->fieldsCache=array();
|
||||
$this->saveCsd=array();
|
||||
$this->countFields=array();
|
||||
$this->preBinds=array();
|
||||
$this->fieldsCache=[];
|
||||
$this->saveCsd=[];
|
||||
$this->countFields=[];
|
||||
$this->preBinds=[];
|
||||
$this->vFields=[];
|
||||
$this->grp_stack=null;
|
||||
// set default values
|
||||
if (($this->dbsType == 'jig' || $this->dbsType == 'mongo')
|
||||
@@ -2132,6 +2222,8 @@ class Cortex extends Cursor {
|
||||
* @return mixed
|
||||
*/
|
||||
public function changed($key=null) {
|
||||
if ($key=='_id')
|
||||
$key = $this->primary;
|
||||
if (method_exists($this->mapper,'changed'))
|
||||
return $this->mapper->changed($key);
|
||||
else
|
||||
@@ -2245,7 +2337,7 @@ class CortexQueryParser extends \Prefab {
|
||||
if (is_int(strpos($where, ':')))
|
||||
list($parts, $args) = $this->convertNamedParams($parts, $args);
|
||||
foreach ($parts as &$part) {
|
||||
$part = $this->_mongo_parse_relational_op($part, $args, $fieldConf);
|
||||
$part = $this->_mongo_parse_relational_op($part, $args, $db, $fieldConf);
|
||||
unset($part);
|
||||
}
|
||||
$ncond = $this->_mongo_parse_logical_op($parts);
|
||||
@@ -2341,18 +2433,19 @@ class CortexQueryParser extends \Prefab {
|
||||
// https://www.debuggex.com/r/6AXwJ1Y3Aac8aocQ/3
|
||||
// https://regex101.com/r/yM5vK4/1
|
||||
// this took me lots of sleepless nights
|
||||
return preg_replace_callback('/'.
|
||||
'(\w+\((?:[^)(]+|(?R))*\))|'. // exclude SQL function names "foo("
|
||||
$out = preg_replace_callback('/'.
|
||||
'\w+\((?:(?>[^()]+)|\((?:(?>[^()]+)|^(?R))*\))*\)|'. // exclude SQL function names "foo("
|
||||
'(?:(\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($db) {
|
||||
if (!isset($match[2]))
|
||||
if (!isset($match[1]))
|
||||
return $match[0];
|
||||
if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[1]))
|
||||
return $match[1];
|
||||
if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[2]))
|
||||
return $match[2];
|
||||
return $db->quotekey($match[2]);
|
||||
return $db->quotekey($match[1]);
|
||||
}, $cond);
|
||||
return $out ?: $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2362,8 +2455,8 @@ class CortexQueryParser extends \Prefab {
|
||||
* @return string
|
||||
*/
|
||||
public function sql_prependTableToFields($cond, $table) {
|
||||
return preg_replace_callback('/'.
|
||||
'(\w+\((?:[^)(]+|(?R))*\))|'.
|
||||
$out = preg_replace_callback('/'.
|
||||
'(\w+\((?:[^)(]+|\((?:[^)(]+|(?R))*\))*\))|'.
|
||||
'(?:(\s)|^|(?<=[(]))'.
|
||||
'([a-zA-Z_](?:[\w\-_]+))'.
|
||||
'(?=[\s<>=!)]|$)/i',
|
||||
@@ -2374,6 +2467,7 @@ class CortexQueryParser extends \Prefab {
|
||||
return $match[0];
|
||||
return $match[2].$table.'.'.$match[3];
|
||||
}, $cond);
|
||||
return $out ?: $cond;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2391,7 +2485,7 @@ class CortexQueryParser extends \Prefab {
|
||||
if (preg_match('/\s*\b(AND|OR)\b\s*/i',$part))
|
||||
continue;
|
||||
// prefix field names
|
||||
$part = preg_replace('/([a-z_-]+)/i', '@$1', $part, -1, $count);
|
||||
$part = preg_replace('/([a-z_-]+(?:[\w-]+))/i', '@$1', $part, -1, $count);
|
||||
// value comparison
|
||||
if (is_int(strpos($part, '?'))) {
|
||||
$val = array_shift($args);
|
||||
@@ -2401,7 +2495,7 @@ class CortexQueryParser extends \Prefab {
|
||||
if (is_int(strpos($upart = strtoupper($part), ' @LIKE '))) {
|
||||
if ($not = is_int($npos = strpos($upart, '@NOT')))
|
||||
$pos = $npos;
|
||||
$val = $this->_likeValueToRegEx($val);
|
||||
$val = '/'.$this->_likeValueToRegEx($val).'/iu';
|
||||
$part = ($not ? '!' : '').'preg_match(?,'.$match[0].')';
|
||||
} // find IN operator
|
||||
elseif (is_int($pos = strpos($upart, ' @IN '))) {
|
||||
@@ -2495,10 +2589,11 @@ class CortexQueryParser extends \Prefab {
|
||||
* find and convert relational operators
|
||||
* @param $part
|
||||
* @param $args
|
||||
* @param \DB\Mongo $db
|
||||
* @param null $fieldConf
|
||||
* @return array|null
|
||||
*/
|
||||
protected function _mongo_parse_relational_op($part, &$args, $fieldConf=null) {
|
||||
protected function _mongo_parse_relational_op($part, &$args, \DB\Mongo $db, $fieldConf=null) {
|
||||
if (is_null($part))
|
||||
return $part;
|
||||
if (preg_match('/\<\=|\>\=|\<\>|\<|\>|\!\=|\=\=|\=|like|not like|in|not in/i', $part, $match)) {
|
||||
@@ -2516,17 +2611,21 @@ class CortexQueryParser extends \Prefab {
|
||||
if ($key == '_id' || (isset($fieldConf[$key]) && isset($fieldConf[$key]['relType']))) {
|
||||
if (is_array($var))
|
||||
foreach ($var as &$id) {
|
||||
if (!$id instanceof \MongoId)
|
||||
if ($db->legacy() && !$id instanceof \MongoId)
|
||||
$id = new \MongoId($id);
|
||||
elseif (!$db->legacy() && !$id instanceof \MongoDB\BSON\ObjectId)
|
||||
$id = new \MongoDB\BSON\ObjectId($id);
|
||||
unset($id);
|
||||
}
|
||||
elseif(!$var instanceof \MongoId)
|
||||
elseif($db->legacy() && !$var instanceof \MongoId)
|
||||
$var = new \MongoId($var);
|
||||
elseif(!$db->legacy() && !$var instanceof \MongoDB\BSON\ObjectId)
|
||||
$var = new \MongoDB\BSON\ObjectId($var);
|
||||
}
|
||||
// find LIKE operator
|
||||
if (in_array($upart, array('LIKE','NOT LIKE'))) {
|
||||
$rgx = $this->_likeValueToRegEx($var);
|
||||
$var = new \MongoRegex($rgx);
|
||||
$var = $db->legacy() ? new \MongoRegex('/'.$rgx.'/iu') : new \MongoDB\BSON\Regex($rgx,'iu');
|
||||
if ($upart == 'NOT LIKE')
|
||||
$var = array('$not' => $var);
|
||||
} // find IN operator
|
||||
@@ -2561,7 +2660,7 @@ class CortexQueryParser extends \Prefab {
|
||||
// %var -> /var$/
|
||||
elseif ($var[0] == '%')
|
||||
$var = substr($var, 1).'$';
|
||||
return '/'.$var.'/iu';
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2573,16 +2672,18 @@ class CortexQueryParser extends \Prefab {
|
||||
*
|
||||
* @param array $options
|
||||
* @param string $engine
|
||||
* @param object $db
|
||||
* @return array|null
|
||||
*/
|
||||
public function prepareOptions($options, $engine) {
|
||||
public function prepareOptions($options, $engine, $db) {
|
||||
if (empty($options) || !is_array($options))
|
||||
return null;
|
||||
switch ($engine) {
|
||||
case 'jig':
|
||||
if (array_key_exists('order', $options))
|
||||
$options['order'] = str_replace(array('asc', 'desc'),
|
||||
array('SORT_ASC', 'SORT_DESC'), strtolower($options['order']));
|
||||
$options['order'] = preg_replace(
|
||||
['/(?<=\h)(ASC)(?=\W|$)/i','/(?<=\h)(DESC)(?=\W|$)/i'],
|
||||
['SORT_ASC','SORT_DESC'],$options['order']);
|
||||
break;
|
||||
case 'mongo':
|
||||
if (array_key_exists('order', $options)) {
|
||||
@@ -2598,12 +2699,27 @@ class CortexQueryParser extends \Prefab {
|
||||
if (array_key_exists('group', $options) && is_string($options['group'])) {
|
||||
$keys = explode(',',$options['group']);
|
||||
$options['group']=array('keys'=>array(),'initial'=>array(),
|
||||
'reduce'=>'function (obj, prev) {}','finalize'=>'');
|
||||
'reduce'=>'function (obj, prev) {}','finalize'=>'');
|
||||
$keys = array_combine($keys,array_fill(0,count($keys),1));
|
||||
$options['group']['keys']=$keys;
|
||||
$options['group']['initial']=$keys;
|
||||
}
|
||||
break;
|
||||
case 'sql':
|
||||
$char=substr($db->quotekey(''),0,1);
|
||||
if (array_key_exists('order', $options) &&
|
||||
FALSE===strpos($options['order'],$char))
|
||||
$options['order']=preg_replace_callback(
|
||||
'/(\w+\h?\(|'. // skip function names
|
||||
'\b(?!\w+)(?:\s+\w+)+|' . // skip field args
|
||||
'\)\s+\w+)|'. // skip function args
|
||||
'(\b\d?[a-zA-Z_](?:[\w\-.])*)/i', // match table/field keys
|
||||
function($match) use($db) {
|
||||
if (!isset($match[2]))
|
||||
return $match[1];
|
||||
return $db->quotekey($match[2]);
|
||||
}, $options['order']);
|
||||
break;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
@@ -2702,7 +2818,7 @@ class CortexCollection extends \ArrayIterator {
|
||||
if (!$this->hasRelSet($prop) || !($relSet = $this->getRelSet($prop)))
|
||||
return null;
|
||||
foreach ($keys as &$key) {
|
||||
if ($key instanceof \MongoId)
|
||||
if ($key instanceof \MongoId || $key instanceof \MongoDB\BSON\ObjectId)
|
||||
$key = (string) $key;
|
||||
unset($key);
|
||||
}
|
||||
|
||||
@@ -141,8 +141,10 @@ class Map extends Controller\AccessController {
|
||||
$wormholes = Model\BasicModel::getNew('WormholeModel');
|
||||
$rows = $wormholes->find('id > 0', null, $expireTimeSQL);
|
||||
$wormholesData = [];
|
||||
foreach((array)$rows as $rowData){
|
||||
$wormholesData[$rowData->name] = $rowData->getData();
|
||||
if($rows){
|
||||
foreach((array)$rows as $rowData){
|
||||
$wormholesData[$rowData->name] = $rowData->getData();
|
||||
}
|
||||
}
|
||||
$return->wormholes = $wormholesData;
|
||||
|
||||
|
||||
134
app/main/controller/api/setup.php
Normal file
134
app/main/controller/api/setup.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 27.05.2018
|
||||
* Time: 14:17
|
||||
*/
|
||||
|
||||
namespace Controller\Api;
|
||||
|
||||
use Controller;
|
||||
use DB\Database;
|
||||
use Model;
|
||||
|
||||
class Setup extends Controller\Controller {
|
||||
|
||||
/**
|
||||
* build search index from existing data (e.g. Systems)
|
||||
* OR import data from ESI (e.g. Structures)
|
||||
* -> optional build/import smaller chunks of data
|
||||
* @param \Base $f3
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function buildIndex(\Base $f3){
|
||||
$postData = (array)$f3->get('POST');
|
||||
$type = (string)$postData['type'];
|
||||
$count = (int)$postData['count'];
|
||||
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
$return->type = $type;
|
||||
$return->count = $count;
|
||||
$return->countAll = 0;
|
||||
$return->countBuild = 0;
|
||||
$return->countBuildAll = 0;
|
||||
$return->progress = 0;
|
||||
|
||||
/**
|
||||
* sum array values
|
||||
* @param int $carry
|
||||
* @param int $value
|
||||
* @return int
|
||||
*/
|
||||
$sum = function(int $carry, int $value){
|
||||
$carry += $value;
|
||||
return $carry;
|
||||
};
|
||||
|
||||
/**
|
||||
* calc percent
|
||||
* @param int $countAll
|
||||
* @param int $count
|
||||
* @return int
|
||||
*/
|
||||
$percent = function(int $countAll, int $count){
|
||||
return $countAll ? floor((100/$countAll) * $count) : 0;
|
||||
};
|
||||
|
||||
$controller = new Controller\Ccp\Universe();
|
||||
switch($type){
|
||||
case 'Systems':
|
||||
$length = 200;
|
||||
$offset = $count * $length;
|
||||
$buildInfo = $controller->buildSystemsIndex($offset, $length);
|
||||
$return->countAll = $buildInfo['countAll'];
|
||||
$return->countBuild = $buildInfo['countBuild'];
|
||||
$return->countBuildAll = count($controller->getSystemsIndex());
|
||||
$return->progress = $percent($return->countAll, $offset + $length);
|
||||
break;
|
||||
case 'Structures':
|
||||
$categoryId = 65;
|
||||
$length = 2;
|
||||
$offset = $count * $length;
|
||||
$buildInfo = $controller->setupCategory($categoryId, $offset, $length);
|
||||
|
||||
$categoryUniverseModel = Model\Universe\BasicUniverseModel::getNew('CategoryModel');
|
||||
$return->countAll = (int)$f3->get('REQUIREMENTS.DATA.STRUCTURES');
|
||||
$return->countBuild = array_reduce($buildInfo, $sum, 0);
|
||||
$return->countBuildAll = $categoryUniverseModel->getById($categoryId, 0)->getTypesCount(false);
|
||||
$return->progress = $percent($return->countAll, $return->countBuildAll);
|
||||
break;
|
||||
case 'Ships':
|
||||
$categoryId = 6;
|
||||
$length = 2;
|
||||
$offset = $count * $length;
|
||||
$buildInfo = $controller->setupCategory($categoryId, $offset, $length);
|
||||
|
||||
$categoryUniverseModel = Model\Universe\BasicUniverseModel::getNew('CategoryModel');
|
||||
$return->countAll = (int)$f3->get('REQUIREMENTS.DATA.SHIPS');
|
||||
$return->countBuild = array_reduce($buildInfo, $sum, 0);
|
||||
$return->countBuildAll = $categoryUniverseModel->getById($categoryId, 0)->getTypesCount(false);
|
||||
$return->progress = $percent($return->countAll, $return->countBuildAll);
|
||||
break;
|
||||
}
|
||||
|
||||
if($return->countBuildAll < $return->countAll){
|
||||
$return->count++;
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* clear search index
|
||||
* @param \Base $f3
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function clearIndex(\Base $f3){
|
||||
$postData = (array)$f3->get('POST');
|
||||
$type = (string)$postData['type'];
|
||||
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
$return->type = $type;
|
||||
$return->count = 0;
|
||||
$return->countAll = 0;
|
||||
$return->countBuild = 0;
|
||||
$return->countBuildAll = 0;
|
||||
$return->progress = 0;
|
||||
|
||||
$controller = new Controller\Ccp\Universe();
|
||||
switch($type) {
|
||||
case 'Systems':
|
||||
$controller->clearSystemsIndex();
|
||||
$systemUniverseModel = Model\Universe\BasicUniverseModel::getNew('SystemModel');
|
||||
$return->countAll = Database::instance()->getRowCount($systemUniverseModel->getTable(), 'UNIVERSE');
|
||||
break;
|
||||
}
|
||||
|
||||
echo json_encode($return);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Controller\Ccp\Universe;
|
||||
use data\filesystem\Search;
|
||||
use DB;
|
||||
use DB\SQL;
|
||||
@@ -111,8 +112,14 @@ class Setup extends Controller {
|
||||
'Model\Universe\GroupModel',
|
||||
'Model\Universe\CategoryModel',
|
||||
'Model\Universe\StructureModel',
|
||||
//'Model\Universe\RegionModel',
|
||||
//'Model\Universe\ConstellationModel'
|
||||
'Model\Universe\WormholeModel',
|
||||
'Model\Universe\StargateModel',
|
||||
'Model\Universe\StarModel',
|
||||
'Model\Universe\PlanetModel',
|
||||
'Model\Universe\SystemModel',
|
||||
'Model\Universe\ConstellationModel',
|
||||
'Model\Universe\RegionModel',
|
||||
'Model\Universe\SystemStaticModel'
|
||||
],
|
||||
'tables' => []
|
||||
],
|
||||
@@ -267,7 +274,7 @@ class Setup extends Controller {
|
||||
$f3->set('socketInformation', $this->getSocketInformation());
|
||||
|
||||
// set index information
|
||||
$f3->set('indexInformation', $this->getIndexData());
|
||||
$f3->set('indexInformation', $this->getIndexData($f3));
|
||||
|
||||
// set cache size
|
||||
$f3->set('cacheSize', $this->getCacheData($f3));
|
||||
@@ -932,7 +939,7 @@ class Setup extends Controller {
|
||||
foreach($requiredTables as $requiredTableName => $data){
|
||||
|
||||
$tableExists = false;
|
||||
$tableEmpty = true;
|
||||
$tableRows = 0;
|
||||
// Check if table status is OK (no errors/warnings,..)
|
||||
$tableStatusCheckCount = 0;
|
||||
|
||||
@@ -944,8 +951,7 @@ class Setup extends Controller {
|
||||
$tableModifierTemp = new MySQL\TableModifier($requiredTableName, $schema);
|
||||
$currentColumns = $tableModifierTemp->getCols(true);
|
||||
// get row count
|
||||
$countRes = $db->exec("SELECT COUNT(*) `num` FROM " . $db->quotekey($requiredTableName) );
|
||||
$tableEmpty = $countRes[0]['num'] > 0 ? false : true;
|
||||
$tableRows = $this->dbLib->getRowCount($requiredTableName, $dbKey);
|
||||
}else{
|
||||
// table missing
|
||||
$dbStatusCheckCount++;
|
||||
@@ -1124,7 +1130,7 @@ class Setup extends Controller {
|
||||
}
|
||||
|
||||
$dbStatusCheckCount += $tableStatusCheckCount;
|
||||
$requiredTables[$requiredTableName]['empty'] = $tableEmpty;
|
||||
$requiredTables[$requiredTableName]['rows'] = $tableRows;
|
||||
$requiredTables[$requiredTableName]['exists'] = $tableExists;
|
||||
$requiredTables[$requiredTableName]['statusCheckCount'] = $tableStatusCheckCount;
|
||||
}
|
||||
@@ -1339,76 +1345,136 @@ class Setup extends Controller {
|
||||
return $socketInformation;
|
||||
}
|
||||
|
||||
/** get indexed (cache) data information
|
||||
/**
|
||||
* get indexed (cache) data information
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getIndexData(){
|
||||
protected function getIndexData(\Base $f3){
|
||||
// active DB and tables are required for obtain index data
|
||||
if(!$this->databaseHasError){
|
||||
$categoryUniverseModel = Model\Universe\BasicUniverseModel::getNew('CategoryModel');
|
||||
$systemUniverseModel = Model\Universe\BasicUniverseModel::getNew('SystemModel');
|
||||
$systemNeighbourModel = Model\BasicModel::getNew('SystemNeighbourModel');
|
||||
$wormholeModel = Model\BasicModel::getNew('WormholeModel');
|
||||
$systemWormholeModel = Model\BasicModel::getNew('SystemWormholeModel');
|
||||
$constellationWormholeModel = Model\BasicModel::getNew('ConstellationWormholeModel');
|
||||
|
||||
$indexInfo = [
|
||||
'SystemNeighbourModel' => [
|
||||
'Systems' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'clearIndex',
|
||||
'label' => 'Clear',
|
||||
'icon' => 'fa-times',
|
||||
'btn' => 'btn-danger'
|
||||
],[
|
||||
'action' => 'buildIndex',
|
||||
'label' => 'build',
|
||||
'label' => 'Build',
|
||||
'icon' => 'fa-sync',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('SystemNeighbourModel')->getTable(),
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('SystemNeighbourModel')->getTable() )
|
||||
'label' => 'build systems index',
|
||||
'countBuild' => count((new Universe())->getSystemsIndex()),
|
||||
'countAll' => $this->dbLib->getRowCount($systemUniverseModel->getTable(), 'UNIVERSE'),
|
||||
'tooltip' => 'build up a static search index over all systems found on DB. Do not refresh page until import is complete (check progress)! Runtime: ~5min'
|
||||
],
|
||||
'Structures' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'buildIndex',
|
||||
'label' => 'Import',
|
||||
'icon' => 'fa-sync',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'label' => 'import structures data',
|
||||
'countBuild' => $categoryUniverseModel->getById(65, 0)->getTypesCount(false),
|
||||
'countAll' => (int)$f3->get('REQUIREMENTS.DATA.STRUCTURES'),
|
||||
'tooltip' => 'import all structure types (e.g. Citadels) from ESI. Runtime: ~15s'
|
||||
],
|
||||
'Ships' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'buildIndex',
|
||||
'label' => 'Import',
|
||||
'icon' => 'fa-sync',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'label' => 'import ships data',
|
||||
'countBuild' => $categoryUniverseModel->getById(6, 0)->getTypesCount(false),
|
||||
'countAll' => (int)$f3->get('REQUIREMENTS.DATA.SHIPS'),
|
||||
'tooltip' => 'import all ships types from ESI. Runtime: ~2min'
|
||||
],
|
||||
'SystemNeighbourModel' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'buildIndex',
|
||||
'label' => 'Build',
|
||||
'icon' => 'fa-sync',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'label' => 'system_neighbour',
|
||||
'countBuild' => $this->dbLib->getRowCount($systemNeighbourModel->getTable()),
|
||||
'countAll' => 5214
|
||||
],
|
||||
'WormholeModel' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'exportTable',
|
||||
'label' => 'export',
|
||||
'label' => 'Export',
|
||||
'icon' => 'fa-download',
|
||||
'btn' => 'btn-default'
|
||||
],[
|
||||
'action' => 'importTable',
|
||||
'label' => 'import',
|
||||
'label' => 'Import',
|
||||
'icon' => 'fa-upload',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('WormholeModel')->getTable(),
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('WormholeModel')->getTable() )
|
||||
'label' => 'wormhole',
|
||||
'countBuild' => $this->dbLib->getRowCount($wormholeModel->getTable()),
|
||||
'countAll' => 89
|
||||
],
|
||||
'SystemWormholeModel' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'exportTable',
|
||||
'label' => 'export',
|
||||
'label' => 'Export',
|
||||
'icon' => 'fa-download',
|
||||
'btn' => 'btn-default'
|
||||
],[
|
||||
'action' => 'importTable',
|
||||
'label' => 'import',
|
||||
'label' => 'Import',
|
||||
'icon' => 'fa-upload',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('SystemWormholeModel')->getTable(),
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('SystemWormholeModel')->getTable() )
|
||||
'label' => 'system_wormhole',
|
||||
'countBuild' => $this->dbLib->getRowCount($systemWormholeModel->getTable()),
|
||||
'countAll' => 233
|
||||
],
|
||||
'ConstellationWormholeModel' => [
|
||||
'task' => [
|
||||
[
|
||||
'action' => 'exportTable',
|
||||
'label' => 'export',
|
||||
'label' => 'Export',
|
||||
'icon' => 'fa-download',
|
||||
'btn' => 'btn-default'
|
||||
],[
|
||||
'action' => 'importTable',
|
||||
'label' => 'import',
|
||||
'label' => 'Import',
|
||||
'icon' => 'fa-upload',
|
||||
'btn' => 'btn-primary'
|
||||
]
|
||||
],
|
||||
'table' => Model\BasicModel::getNew('ConstellationWormholeModel')->getTable(),
|
||||
'count' => $this->dbLib->getRowCount( Model\BasicModel::getNew('ConstellationWormholeModel')->getTable() )
|
||||
'label' => 'constellation_wormhole',
|
||||
'countBuild' => $this->dbLib->getRowCount( $constellationWormholeModel->getTable() ),
|
||||
'countAll' => 460
|
||||
]
|
||||
];
|
||||
}else{
|
||||
|
||||
246
app/main/cron/universe.php
Normal file
246
app/main/cron/universe.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 19.05.2018
|
||||
* Time: 03:46
|
||||
*/
|
||||
|
||||
namespace Cron;
|
||||
|
||||
use Model;
|
||||
|
||||
class Universe {
|
||||
|
||||
const LOG_TEXT = '%s type: %s %s/%s peak: %s total: %s msg: %s';
|
||||
|
||||
|
||||
/**
|
||||
* format counter for output (min length)
|
||||
* @param int $counter
|
||||
* @return string
|
||||
*/
|
||||
private function formatCounterValue(int $counter){
|
||||
return str_pad($counter, 4, ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* format id for output (min length)
|
||||
* @param int $counter
|
||||
* @return string
|
||||
*/
|
||||
private function formatIdValue(int $counter){
|
||||
return str_pad($counter, 10, ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* format Byte §size for output
|
||||
* @param int $size
|
||||
* @return string
|
||||
*/
|
||||
private function formatMemoryValue(int $size){
|
||||
$unit = ['B','KB','MB','GB','TB','PB'];
|
||||
return str_pad(number_format(@round($size/pow(1024,($i=floor(log($size,1024)))),2), 2, '.', '') . '' . $unit[$i], 9, ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* format seconds for output
|
||||
* @param float $time
|
||||
* @return string
|
||||
*/
|
||||
private function formatSeconds(float $time){
|
||||
$time = round($time, 5);
|
||||
$formatSeconds = function($seconds){
|
||||
return str_pad(number_format(round($seconds, 5), 5), 8, ' ', STR_PAD_LEFT);
|
||||
};
|
||||
|
||||
$formatted = $time < 60 ? $formatSeconds($time) . 's' : floor($time / 60) . 'm ' . $formatSeconds(fmod($time, 60)) . 's';
|
||||
return str_pad($formatted, 14, ' ', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* flush output
|
||||
*/
|
||||
private function echoFlush(){
|
||||
flush();
|
||||
ob_flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo input parameters
|
||||
* @param string $type
|
||||
* @param int $paramOffset
|
||||
* @param int $paramLength
|
||||
*/
|
||||
private function echoParams(string $type, int $paramOffset, int $paramLength){
|
||||
echo 'params ───────────────────────────────────────────────────────────────────────────────────────────────────────' . PHP_EOL;
|
||||
echo 'type : ' . $type . PHP_EOL;
|
||||
echo 'offset : ' . $paramOffset . PHP_EOL;
|
||||
echo 'length : ' . $paramLength . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo configuration
|
||||
*/
|
||||
private function echoConfig(){
|
||||
echo 'config ───────────────────────────────────────────────────────────────────────────────────────────────────────' . PHP_EOL;
|
||||
echo 'max_execution_time : ' . ini_get('max_execution_time') . PHP_EOL;
|
||||
echo 'memory_limit : ' . ini_get('memory_limit') . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo information
|
||||
* @param int $total
|
||||
* @param int $offset
|
||||
* @param int $importCount
|
||||
* @param array $ids
|
||||
*/
|
||||
private function echoInfo(int $total, int $offset, int $importCount, array $ids){
|
||||
echo 'info ─────────────────────────────────────────────────────────────────────────────────────────────────────────' . PHP_EOL;
|
||||
echo 'all data : ' . $total . PHP_EOL;
|
||||
echo 'import offset : ' . $offset . PHP_EOL;
|
||||
echo 'import count : ' . $importCount . PHP_EOL;
|
||||
echo 'import chunk : ' . implode(',', $ids) . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo start
|
||||
*/
|
||||
private function echoStart(){
|
||||
echo 'start ────────────────────────────────────────────────────────────────────────────────────────────────────────' . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo loop start information
|
||||
* @param int $count
|
||||
* @param int $importCount
|
||||
* @param int $id
|
||||
*/
|
||||
private function echoLoading(int $count, int $importCount, int $id){
|
||||
echo '[' . date('H:i:s') . '] loading... ' . $this->formatCounterValue($count) . '/' . $importCount . ' id: ' . $this->formatIdValue($id) . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo loop finish information
|
||||
* @param int $importCount
|
||||
* @param int $id
|
||||
* @param float $timeLoopStart
|
||||
* @param float $timeTotalStart
|
||||
*/
|
||||
private function echoLoaded(int $importCount, int $id, float $timeLoopStart, float $timeTotalStart){
|
||||
echo '[' . date('H:i:s') . '] loaded ' . str_pad('', strlen($importCount), ' ') . ' id: ' . $this->formatIdValue($id) .
|
||||
' memory: ' . $this->formatMemoryValue(memory_get_usage()) .
|
||||
' time: ' . $this->formatSeconds(microtime(true) - $timeLoopStart) .
|
||||
' total: ' . $this->formatSeconds(microtime(true) - $timeTotalStart) . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* echo finish information
|
||||
* @param int $count
|
||||
* @param int $importCount
|
||||
* @param float $timeTotalStart
|
||||
*/
|
||||
private function echoFinish(int $count, int $importCount, float $timeTotalStart){
|
||||
echo 'finished ─────────────────────────────────────────────────────────────────────────────────────────────────────' . PHP_EOL;
|
||||
echo '[' . date('H:i:s') . '] ' . $this->formatCounterValue($count) . '/' . $importCount .
|
||||
' peak: ' . $this->formatMemoryValue(memory_get_peak_usage ()) .
|
||||
' total: ' . $this->formatSeconds(microtime(true) - $timeTotalStart) . PHP_EOL;
|
||||
$this->echoFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* imports static universe data from ESI
|
||||
* >> php index.php "/cron/setup?model=system&offset=0&length=5"
|
||||
* @param \Base $f3
|
||||
* @throws \Exception
|
||||
*/
|
||||
function setup(\Base $f3){
|
||||
$params = (array)$f3->get('GET');
|
||||
$type = (string)$params['model'];
|
||||
$paramOffset = (int)$params['offset'];
|
||||
$paramLength = (int)$params['length'];
|
||||
$timeTotalStart = microtime(true);
|
||||
$msg = '';
|
||||
|
||||
$ids = [];
|
||||
$count = 0;
|
||||
$importCount = [];
|
||||
$modelClass = '';
|
||||
$setupModel = function(Model\Universe\BasicUniverseModel &$model, int $id){};
|
||||
|
||||
switch($type){
|
||||
case 'system':
|
||||
// load systems + dependencies (planets, star, types,...)
|
||||
$ids = $f3->ccpClient->getUniverseSystems();
|
||||
$modelClass = 'SystemModel';
|
||||
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
|
||||
$model->loadById($id);
|
||||
$model->loadPlanetsData();
|
||||
};
|
||||
break;
|
||||
case 'stargate':
|
||||
// load all stargates. Systems must be present first!
|
||||
$ids = $f3->ccpClient->getUniverseSystems();
|
||||
$modelClass = 'SystemModel';
|
||||
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
|
||||
$model->loadById($id);
|
||||
$model->loadStargatesData();
|
||||
};
|
||||
break;
|
||||
case 'index_system':
|
||||
// setup system index, Systems must be present first!
|
||||
$ids = $f3->ccpClient->getUniverseSystems();
|
||||
$modelClass = 'SystemModel';
|
||||
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
|
||||
$model->getById($id); // no loadById() here! would take "forever" when system not exists and build up first...
|
||||
$model->buildIndex();
|
||||
};
|
||||
break;
|
||||
default:
|
||||
$msg = 'Model is not valid';
|
||||
}
|
||||
|
||||
if($modelClass){
|
||||
$this->echoParams($type, $paramOffset, $paramLength);
|
||||
$this->echoConfig();
|
||||
|
||||
$total = count($ids);
|
||||
$offset = ($paramOffset < 0) ? 0 : (($paramOffset >= $total) ? $total : $paramOffset);
|
||||
$length = ($paramLength < 0) ? 0 : $paramLength;
|
||||
sort($ids, SORT_NUMERIC);
|
||||
$ids = array_slice($ids, $offset, $length);
|
||||
$importCount = count($ids);
|
||||
|
||||
$this->echoInfo($total, $offset, $importCount, $ids);
|
||||
$this->echoStart();
|
||||
|
||||
/**
|
||||
* @var $model Model\Universe\SystemModel
|
||||
*/
|
||||
$model = Model\Universe\BasicUniverseModel::getNew($modelClass);
|
||||
foreach($ids as $id){
|
||||
$timeLoopStart = microtime(true);
|
||||
$this->echoLoading(++$count, $importCount, $id);
|
||||
$setupModel($model, $id);
|
||||
$model->reset();
|
||||
$this->echoLoaded($importCount, $id, $timeLoopStart, $timeTotalStart);
|
||||
}
|
||||
|
||||
$this->echoFinish($count, $importCount, $timeTotalStart);
|
||||
}
|
||||
|
||||
// Log --------------------------------------------------------------------------------------------------------
|
||||
$log = new \Log('cron_' . __FUNCTION__ . '.log');
|
||||
$log->write( sprintf(self::LOG_TEXT, __FUNCTION__, $type,
|
||||
$this->formatCounterValue($count), $importCount, $this->formatMemoryValue(memory_get_peak_usage ()),
|
||||
$this->formatSeconds(microtime(true) - $timeTotalStart), $msg) );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -35,6 +35,7 @@ class CcpClient extends \Prefab {
|
||||
$client->setDatasource( Config::getEnvironmentData('CCP_ESI_DATASOURCE') );
|
||||
$client->setUserAgent($this->getUserAgent());
|
||||
$client->setDebugLevel($f3->get('DEBUG'));
|
||||
//$client->setDebugLogRequests(true);
|
||||
}else{
|
||||
LogController::getLogger('ERROR')->write(sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class));
|
||||
}
|
||||
|
||||
@@ -47,32 +47,6 @@ abstract class AbstractMapTrackingModel extends BasicModel implements LogModelIn
|
||||
return array_merge(parent::getStaticFieldConf(), $this->trackingFieldConf);
|
||||
}
|
||||
|
||||
/**
|
||||
* validates a model field to be a valid relational model
|
||||
* @param $key
|
||||
* @param $val
|
||||
* @return bool
|
||||
* @throws \Exception\ValidationException
|
||||
*/
|
||||
protected function validate_notDry($key, $val): bool {
|
||||
$valid = true;
|
||||
if($colConf = $this->fieldConf[$key]){
|
||||
if(isset($colConf['belongs-to-one'])){
|
||||
if( (is_int($val) || ctype_digit($val)) && (int)$val > 0){
|
||||
$valid = true;
|
||||
}elseif( is_a($val, $colConf['belongs-to-one']) && !$val->dry() ){
|
||||
$valid = true;
|
||||
}else{
|
||||
$valid = false;
|
||||
$msg = 'Validation failed: "' . get_class($this) . '->' . $key . '" must be a valid instance of ' . $colConf['belongs-to-one'];
|
||||
$this->throwValidationException($key, $msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* log character activity create/update/delete events
|
||||
* @param string $action
|
||||
|
||||
@@ -111,7 +111,7 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
|
||||
const ERROR_INVALID_MODEL_CLASS = 'Model class (%s) not found';
|
||||
|
||||
public function __construct($db = NULL, $table = NULL, $fluid = NULL, $ttl = 0){
|
||||
public function __construct($db = NULL, $table = NULL, $fluid = NULL, $ttl = self::DEFAULT_TTL){
|
||||
|
||||
$this->addStaticFieldConfig();
|
||||
|
||||
@@ -318,6 +318,32 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* validates a model field to be a valid relational model
|
||||
* @param $key
|
||||
* @param $val
|
||||
* @return bool
|
||||
* @throws \Exception\ValidationException
|
||||
*/
|
||||
protected function validate_notDry($key, $val): bool {
|
||||
$valid = true;
|
||||
if($colConf = $this->fieldConf[$key]){
|
||||
if(isset($colConf['belongs-to-one'])){
|
||||
if( (is_int($val) || ctype_digit($val)) && (int)$val > 0){
|
||||
$valid = true;
|
||||
}elseif( is_a($val, $colConf['belongs-to-one']) && !$val->dry() ){
|
||||
$valid = true;
|
||||
}else{
|
||||
$valid = false;
|
||||
$msg = 'Validation failed: "' . get_class($this) . '->' . $key . '" must be a valid instance of ' . $colConf['belongs-to-one'];
|
||||
$this->throwValidationException($key, $msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* get key for for all objects in this table
|
||||
* @return string
|
||||
@@ -619,15 +645,21 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
/**
|
||||
* export and download table data as *.csv
|
||||
* this is primarily used for static tables
|
||||
* @param array $fields
|
||||
* @return bool
|
||||
*/
|
||||
public function exportData(){
|
||||
public function exportData(array $fields = []){
|
||||
$status = false;
|
||||
|
||||
if(static::$enableDataExport){
|
||||
$tableModifier = static::getTableModifier();
|
||||
$headers = $tableModifier->getCols();
|
||||
|
||||
if($fields){
|
||||
// columns to export -> reIndex keys
|
||||
$headers = array_values(array_intersect($headers, $fields));
|
||||
}
|
||||
|
||||
// just get the records with existing columns
|
||||
// -> no "virtual" fields or "new" columns
|
||||
$this->fields($headers);
|
||||
@@ -659,14 +691,16 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
|
||||
/**
|
||||
* import table data from a *.csv file
|
||||
* @return bool
|
||||
* @return array|bool
|
||||
*/
|
||||
public function importData(){
|
||||
$status = false;
|
||||
|
||||
// rtrim(); for arrays (removes empty values) from the end
|
||||
$rtrim = function($array = []){
|
||||
return array_slice($array, 0, key(array_reverse($array, 1))+1);
|
||||
$rtrim = function($array = [], $lengthMin = false){
|
||||
$length = key(array_reverse(array_diff($array, ['']), 1))+1;
|
||||
$length = $length < $lengthMin ? $lengthMin : $length;
|
||||
return array_slice($array, 0, $length);
|
||||
};
|
||||
|
||||
if(static::$enableDataImport){
|
||||
@@ -680,7 +714,7 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
if(count($keys) > 0){
|
||||
$tableData = [];
|
||||
while (!feof($handle)) {
|
||||
$tableData[] = array_combine($keys, $rtrim(fgetcsv($handle, 0, ';')));
|
||||
$tableData[] = array_combine($keys, $rtrim(fgetcsv($handle, 0, ';'), count($keys)));
|
||||
}
|
||||
// import row data
|
||||
$status = $this->importStaticData($tableData);
|
||||
@@ -704,20 +738,22 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
*/
|
||||
protected function importStaticData($tableData = []){
|
||||
$rowIDs = [];
|
||||
$columnNames = array_merge(['id'], array_keys($this->fieldConf));
|
||||
$addedCount = 0;
|
||||
$updatedCount = 0;
|
||||
$deletedCount = 0;
|
||||
|
||||
$tableModifier = static::getTableModifier();
|
||||
$fields = $tableModifier->getCols();
|
||||
|
||||
foreach($tableData as $rowData){
|
||||
// search for existing record and update columns
|
||||
$this->getById($rowData['id']);
|
||||
$this->getById($rowData['id'], 0);
|
||||
if($this->dry()){
|
||||
$addedCount++;
|
||||
}else{
|
||||
$updatedCount++;
|
||||
}
|
||||
$this->copyfrom($rowData, $columnNames);
|
||||
$this->copyfrom($rowData, $fields);
|
||||
$this->save();
|
||||
$rowIDs[] = $this->id;
|
||||
$this->reset();
|
||||
@@ -845,6 +881,21 @@ abstract class BasicModel extends \DB\Cortex {
|
||||
return \Base::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* stores data direct into the Cache backend (e.g. Redis)
|
||||
* $f3->set() used the same code. The difference is, that $f3->set()
|
||||
* also loads data into the Hive.
|
||||
* This can result in high RAM usage if a great number of key->values should be stored in Cache
|
||||
* (like the search index for system data)
|
||||
* @param string $key
|
||||
* @param $data
|
||||
* @param int $ttl
|
||||
*/
|
||||
public static function setCacheValue(string $key, $data, int $ttl = 0){
|
||||
$cache = \Cache::instance();
|
||||
$cache->set(self::getF3()->hash($key).'.var', $data, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* debug log function
|
||||
* @param string $text
|
||||
|
||||
@@ -197,8 +197,8 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* get map data
|
||||
* -> this includes system and connection data as well!
|
||||
* get data
|
||||
* -> this includes system and connection data as well
|
||||
* @return \stdClass
|
||||
* @throws PathfinderException
|
||||
* @throws \Exception
|
||||
|
||||
@@ -110,9 +110,7 @@ class StructureModel extends BasicModel {
|
||||
*/
|
||||
public function set_structureId($structureId){
|
||||
$structureId = (int)$structureId;
|
||||
$structureId = $structureId ? : null;
|
||||
|
||||
return $structureId;
|
||||
return $structureId ? : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -242,13 +242,13 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
|
||||
$systemData->created = (object) [];
|
||||
$systemData->created->created = strtotime($this->created);
|
||||
if( is_object($this->createdCharacterId) ){
|
||||
if(is_object($this->createdCharacterId)){
|
||||
$systemData->created->character = $this->createdCharacterId->getData();
|
||||
}
|
||||
|
||||
$systemData->updated = (object) [];
|
||||
$systemData->updated->updated = strtotime($this->updated);
|
||||
if( is_object($this->updatedCharacterId) ){
|
||||
if(is_object($this->updatedCharacterId)){
|
||||
$systemData->updated->character = $this->updatedCharacterId->getData();
|
||||
}
|
||||
|
||||
@@ -701,11 +701,11 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* overwrites parent
|
||||
* @param null $db
|
||||
* @param null $table
|
||||
* @param null $fields
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function setup($db=null, $table=null, $fields=null){
|
||||
$status = parent::setup($db,$table,$fields);
|
||||
|
||||
@@ -18,10 +18,26 @@ abstract class BasicUniverseModel extends BasicModel {
|
||||
* data from Universe tables is static and does not change frequently
|
||||
* -> refresh static data after X days
|
||||
*/
|
||||
const CACHE_MAX_DAYS = 7;
|
||||
const CACHE_MAX_DAYS = 60;
|
||||
|
||||
const CACHE_KEY_PREFIX = 'index_universe_';
|
||||
|
||||
/**
|
||||
* cache key for model data -> should "never" expire
|
||||
* -> until manual remove and or global cache clear
|
||||
*/
|
||||
const CACHE_INDEX_EXPIRE_KEY = 86400 * 356 * 5;
|
||||
|
||||
protected $db = 'DB_UNIVERSE';
|
||||
|
||||
/**
|
||||
* get model data -> should be overwritten
|
||||
* @return null
|
||||
*/
|
||||
public function getData(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event "Hook" function
|
||||
* return false will stop any further action
|
||||
@@ -37,6 +53,78 @@ abstract class BasicUniverseModel extends BasicModel {
|
||||
return parent::beforeUpdateEvent($self, $pkeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* get hashKey for search index build
|
||||
* -> used by the cache backend
|
||||
* @param string $column
|
||||
* @return bool|string
|
||||
*/
|
||||
public function getHashKey(string $column = '_id'){
|
||||
$key = false;
|
||||
if( !$this->dry() && $this->exists($column) ){
|
||||
$key = self::generateHashKeyRow($this->getTable(), $this->$column);
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate time period (in seconds) from now on, until data get expired
|
||||
* @return int
|
||||
*/
|
||||
/*
|
||||
public function calcTtl() : int {
|
||||
$ttl = 0;
|
||||
if(!$this->dry()){
|
||||
$timezone = $this->getF3()->get('getTimeZone')();
|
||||
$currentTime = new \DateTime('now', $timezone);
|
||||
$updateTime = \DateTime::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->updated,
|
||||
$timezone
|
||||
);
|
||||
// add expire period to last updated timestamp
|
||||
$updateTime->modify('+' . self::CACHE_MAX_DAYS . ' day');
|
||||
|
||||
$seconds = $updateTime->getTimestamp() - $currentTime->getTimestamp();
|
||||
if($seconds > 0){
|
||||
$ttl = $seconds;
|
||||
}
|
||||
}
|
||||
|
||||
return $ttl;
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* build up a "search" index for this model
|
||||
* -> stores getData() result into Cache (RAM) for faster access
|
||||
*/
|
||||
public function buildIndex(){
|
||||
$hashKeyId = $this->getHashKey();
|
||||
$hashKeyName = $this->getHashKey('name');
|
||||
if($hashKeyId && $hashKeyName){
|
||||
$f3 = self::getF3();
|
||||
$hashKeyTable = self::generateHashKeyTable($this->getTable());
|
||||
|
||||
if( !$f3->exists($hashKeyTable, $cachedData) ){
|
||||
$cachedData = [];
|
||||
}
|
||||
|
||||
if( !in_array($hashKeyName, $cachedData) ){
|
||||
$cachedData[] = $hashKeyName;
|
||||
}
|
||||
|
||||
// value update does not update ttl -> delete key from cache and add again
|
||||
$f3->clear($hashKeyId);
|
||||
$f3->clear($hashKeyName);
|
||||
$f3->clear($hashKeyTable);
|
||||
|
||||
// straight into cache (no $f->set() ), no sync with hive here -> save ram
|
||||
self::setCacheValue($hashKeyId, $this->getData(), self::CACHE_INDEX_EXPIRE_KEY);
|
||||
self::setCacheValue($hashKeyName, $hashKeyId, self::CACHE_INDEX_EXPIRE_KEY);
|
||||
self::setCacheValue($hashKeyTable, $cachedData, self::CACHE_INDEX_EXPIRE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load object by $id
|
||||
* -> if $id not exists in DB -> query API
|
||||
@@ -54,6 +142,21 @@ abstract class BasicUniverseModel extends BasicModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load data by foreign key or other column than "id"
|
||||
* @param string $key
|
||||
* @param $value
|
||||
*/
|
||||
public function loadByKey(string $key, $value){
|
||||
/**
|
||||
* @var $model self
|
||||
*/
|
||||
$model = $this->getByForeignKey($key, $value, ['limit' => 1]);
|
||||
if($model->isOutdated()){
|
||||
$model->loadDataByKey($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load data from API into $this and save $this
|
||||
* @param int $id
|
||||
@@ -62,6 +165,28 @@ abstract class BasicUniverseModel extends BasicModel {
|
||||
*/
|
||||
abstract protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []);
|
||||
|
||||
protected function loadDataByKey(string $key, $value){}
|
||||
|
||||
/**
|
||||
* generate hashKey for a table row data for search index build
|
||||
* @param string $table
|
||||
* @param $value
|
||||
* @return string
|
||||
*/
|
||||
public static function generateHashKeyRow(string $table, $value){
|
||||
return self::generateHashKeyTable($table) . '_' . md5(strtolower((string)$value));
|
||||
}
|
||||
|
||||
/**
|
||||
* generate hashKey for a complete table
|
||||
* -> should hold hashKeys for multiple rows
|
||||
* @param string $table
|
||||
* @return string
|
||||
*/
|
||||
public static function generateHashKeyTable(string $table){
|
||||
return self::CACHE_KEY_PREFIX . strtolower($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* factory for all UniverseModels
|
||||
* @param string $model
|
||||
|
||||
@@ -49,14 +49,17 @@ class CategoryModel extends BasicUniverseModel {
|
||||
|
||||
/**
|
||||
* get all groups for this category
|
||||
* @param bool $published
|
||||
* @return array|mixed
|
||||
*/
|
||||
protected function getGroups(){
|
||||
protected function getGroups(bool $published = true){
|
||||
$groups = [];
|
||||
$this->filter('groups', [
|
||||
'published = :published',
|
||||
':published' => 1
|
||||
]);
|
||||
if($published){
|
||||
$this->filter('groups', [
|
||||
'published = :published',
|
||||
':published' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
if($this->groups){
|
||||
$groups = $this->groups;
|
||||
@@ -79,6 +82,24 @@ class CategoryModel extends BasicUniverseModel {
|
||||
return $groupsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* count all types that belong to groups in this category
|
||||
* @param bool $published
|
||||
* @return int
|
||||
*/
|
||||
public function getTypesCount(bool $published = true) : int {
|
||||
$count = 0;
|
||||
if( !$this->dry() ){
|
||||
/**
|
||||
* @var $group GroupModel
|
||||
*/
|
||||
foreach($groups = $this->getGroups($published) as $group){
|
||||
$count += $group->getTypesCount($published);
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* load data from API into $this and save $this
|
||||
* @param int $id
|
||||
@@ -95,17 +116,30 @@ class CategoryModel extends BasicUniverseModel {
|
||||
|
||||
/**
|
||||
* load groups data for this category
|
||||
* @param int $offset
|
||||
* @param int $length 0 -> all groups
|
||||
* @return array
|
||||
*/
|
||||
public function loadGroupsData(){
|
||||
public function loadGroupsData(int $offset = 0, int $length = 0) : array {
|
||||
$groupIds = [];
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseCategoryData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['groups'] as $groupId){
|
||||
array_multisort($data['groups'], SORT_ASC, SORT_NUMERIC);
|
||||
if($length){
|
||||
$data['groups'] = array_slice($data['groups'], $offset, $length);
|
||||
}
|
||||
foreach($data['groups'] as $groupId){
|
||||
/**
|
||||
* @var $group GroupModel
|
||||
*/
|
||||
$group = $this->rel('groups');
|
||||
$group->loadById($groupId);
|
||||
$groupIds[] = $groupId;
|
||||
$group->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $groupIds;
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,6 @@ class ConstellationModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'constellation';
|
||||
|
||||
/**
|
||||
* No static columns added
|
||||
* @var bool
|
||||
*/
|
||||
protected $addStaticFields = false;
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
@@ -35,32 +29,94 @@ class ConstellationModel extends BasicUniverseModel {
|
||||
'table' => 'region',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'x' => [
|
||||
'type' => Schema::DT_INT8,
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'y' => [
|
||||
'type' => Schema::DT_INT8,
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'z' => [
|
||||
'type' => Schema::DT_INT8,
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'systems' => [
|
||||
'has-many' => ['Model\Universe\SystemModel', 'constellationId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get data
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData(){
|
||||
$constellationData = (object) [];
|
||||
$constellationData->id = $this->_id;
|
||||
$constellationData->name = $this->name;
|
||||
$constellationData->region = $this->regionId->getData();
|
||||
|
||||
return $constellationData;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for positions array (x/y/z)
|
||||
* @param $position
|
||||
* @return null
|
||||
*/
|
||||
public function set_position($position){
|
||||
$position = (array)$position;
|
||||
if(count($position) === 3){
|
||||
$this->x = $position['x'];
|
||||
$this->y = $position['y'];
|
||||
$this->z = $position['z'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseConstellationData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $region RegionModel
|
||||
*/
|
||||
$region = $this->rel('regionId');
|
||||
$region->loadById($data['regionId'], $accessToken, $additionalOptions);
|
||||
$data['regionId'] = $region;
|
||||
|
||||
$this->copyfrom($data, ['id', 'name', 'regionId', 'position']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load systems data for this constellation
|
||||
*/
|
||||
public function loadSystemsData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseConstellationData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['systems'] as $systemId){
|
||||
/**
|
||||
* @var $system SystemModel
|
||||
*/
|
||||
$system = $this->rel('systems');
|
||||
$system->loadById($systemId);
|
||||
$system->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,7 +35,8 @@ class GroupModel extends BasicUniverseModel {
|
||||
'table' => 'category',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'types' => [
|
||||
'has-many' => ['Model\Universe\TypeModel', 'groupId']
|
||||
@@ -61,14 +62,17 @@ class GroupModel extends BasicUniverseModel {
|
||||
|
||||
/**
|
||||
* get all types for this group
|
||||
* @param bool $published
|
||||
* @return array|mixed
|
||||
*/
|
||||
protected function getTypes(){
|
||||
protected function getTypes(bool $published = true){
|
||||
$types = [];
|
||||
$this->filter('types', [
|
||||
'published = :published',
|
||||
':published' => 1
|
||||
]);
|
||||
if($published){
|
||||
$this->filter('types', [
|
||||
'published = :published',
|
||||
':published' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
if($this->types){
|
||||
$types = $this->types;
|
||||
@@ -91,9 +95,26 @@ class GroupModel extends BasicUniverseModel {
|
||||
return $typesData;
|
||||
}
|
||||
|
||||
/**
|
||||
* count all types in this group
|
||||
* @param bool $published
|
||||
* @return int
|
||||
*/
|
||||
public function getTypesCount(bool $published = true) : int {
|
||||
return $this->dry() ? 0 : count($this->getTypes($published));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseGroupData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $category CategoryModel
|
||||
*/
|
||||
$category = $this->rel('categoryId');
|
||||
$category->loadById($data['categoryId'], $accessToken, $additionalOptions);
|
||||
$data['categoryId'] = $category;
|
||||
@@ -105,17 +126,24 @@ class GroupModel extends BasicUniverseModel {
|
||||
|
||||
/**
|
||||
* load types data for this group
|
||||
* @return int
|
||||
*/
|
||||
public function loadTypesData(){
|
||||
$count = 0;
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseGroupData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['types'] as $typeId){
|
||||
/**
|
||||
* @var $type TypeModel
|
||||
*/
|
||||
$type = $this->rel('types');
|
||||
$type->loadById($typeId);
|
||||
$type->reset();
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
123
app/main/model/universe/planetmodel.php
Normal file
123
app/main/model/universe/planetmodel.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 19.05.2018
|
||||
* Time: 01:12
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class PlanetModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'planet';
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'systemId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\SystemModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'system',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'typeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\TypeModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'type',
|
||||
'on-delete' => 'SET NULL'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'x' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'y' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'z' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get data
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData(){
|
||||
$planetData = (object) [];
|
||||
$planetData->id = $this->_id;
|
||||
$planetData->name = $this->name;
|
||||
|
||||
$planetData->position = (object) [];
|
||||
$planetData->position->x = $this->x;
|
||||
$planetData->position->y = $this->y;
|
||||
$planetData->position->z = $this->z;
|
||||
|
||||
return $planetData;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for positions array (x/y/z)
|
||||
* @param $position
|
||||
* @return null
|
||||
*/
|
||||
public function set_position($position){
|
||||
$position = (array)$position;
|
||||
if(count($position) === 3){
|
||||
$this->x = $position['x'];
|
||||
$this->y = $position['y'];
|
||||
$this->z = $position['z'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniversePlanetData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $system SystemModel
|
||||
*/
|
||||
$system = $this->rel('systemId');
|
||||
$system->loadById($data['systemId'], $accessToken, $additionalOptions);
|
||||
$data['systemId'] = $system;
|
||||
|
||||
/**
|
||||
* @var $type TypeModel
|
||||
*/
|
||||
$type = $this->rel('typeId');
|
||||
$type->loadById($data['typeId'], $accessToken, $additionalOptions);
|
||||
$data['typeId'] = $type;
|
||||
|
||||
$this->copyfrom($data, ['id', 'name', 'systemId', 'typeId', 'position']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,12 +14,6 @@ class RegionModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'region';
|
||||
|
||||
/**
|
||||
* No static columns added
|
||||
* @var bool
|
||||
*/
|
||||
protected $addStaticFields = false;
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
@@ -34,12 +28,47 @@ class RegionModel extends BasicUniverseModel {
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* get data
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData(){
|
||||
$regionData = (object) [];
|
||||
$regionData->id = $this->_id;
|
||||
$regionData->name = $this->name;
|
||||
|
||||
return $regionData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseRegionData($id);
|
||||
if(!empty($data)){
|
||||
$this->copyfrom($data, ['id', 'name', 'description']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load constellations data for this region
|
||||
*/
|
||||
public function loadConstellationsData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseRegionData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['constellations'] as $constellationsId){
|
||||
/**
|
||||
* @var $constellation ConstellationModel
|
||||
*/
|
||||
$constellation = $this->rel('constellations');
|
||||
$constellation->loadById($constellationsId);
|
||||
$constellation->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
164
app/main/model/universe/stargatemodel.php
Normal file
164
app/main/model/universe/stargatemodel.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 19.05.2018
|
||||
* Time: 04:30
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class StargateModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'stargate';
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'systemId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\SystemModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'system',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'typeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\TypeModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'type',
|
||||
'on-delete' => 'SET NULL'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'destinationSystemId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\SystemModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'system',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'x' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'y' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'z' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
]
|
||||
];
|
||||
|
||||
public function getData(){
|
||||
|
||||
$stargateData = (object) [];
|
||||
$stargateData->id = $this->_id;
|
||||
$stargateData->type = $this->typeId->name;
|
||||
$stargateData->destination = $this->destinationSystemId->name;
|
||||
|
||||
return $stargateData;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for positions array (x/y/z)
|
||||
* @param $position
|
||||
* @return null
|
||||
*/
|
||||
public function set_position($position){
|
||||
$position = (array)$position;
|
||||
if(count($position) === 3){
|
||||
$this->x = $position['x'];
|
||||
$this->y = $position['y'];
|
||||
$this->z = $position['z'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseStargateData($id);
|
||||
|
||||
if(!empty($data)){
|
||||
|
||||
if($this->get('systemId', true) !== $data['systemId']){
|
||||
// new stargate or system changed
|
||||
/**
|
||||
* @var $system SystemModel
|
||||
*/
|
||||
$system = $this->rel('systemId');
|
||||
$system->loadById($data['systemId'], $accessToken, $additionalOptions);
|
||||
$data['systemId'] = $system;
|
||||
}
|
||||
|
||||
if($this->get('typeId', true) !== $data['typeId']){
|
||||
/**
|
||||
* @var $type TypeModel
|
||||
*/
|
||||
$type = $this->rel('typeId');
|
||||
$type->loadById($data['typeId'], $accessToken, $additionalOptions);
|
||||
$data['typeId'] = $type;
|
||||
}
|
||||
|
||||
if($this->get('destinationSystemId', true) !== $data['destination']->system_id){
|
||||
// new stargate or destinationSystem changed
|
||||
/**
|
||||
* @var $destinationSystem SystemModel
|
||||
*/
|
||||
$destinationSystem = $this->rel('destinationSystemId');
|
||||
// no loadById() here! we don´t want to insert/update systems that do not exist yet
|
||||
$destinationSystem->getById($data['destination']->system_id, 0);
|
||||
|
||||
if( !$destinationSystem->dry() ){
|
||||
$data['destinationSystemId'] = $destinationSystem;
|
||||
$this->copyfrom($data, ['id', 'name', 'position', 'systemId', 'typeId', 'destinationSystemId']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $db
|
||||
* @param null $table
|
||||
* @param null $fields
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function setup($db=null, $table=null, $fields=null){
|
||||
$status = parent::setup($db,$table,$fields);
|
||||
|
||||
if($status === true){
|
||||
$status = parent::setMultiColumnIndex(['systemId', 'destinationSystemId'], true);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
96
app/main/model/universe/starmodel.php
Normal file
96
app/main/model/universe/starmodel.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 18.05.2018
|
||||
* Time: 23:52
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class StarModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'star';
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'typeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\TypeModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'type',
|
||||
'on-delete' => 'SET NULL'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'age' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'radius' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'temperature' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'luminosity' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'spectralClass' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'system' => [
|
||||
'has-one' => ['Model\Universe\SystemModel', 'starId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get data
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData(){
|
||||
$starData = (object) [];
|
||||
$starData->id = $this->_id;
|
||||
$starData->name = $this->typeId->name;
|
||||
|
||||
return $starData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseStarData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $type TypeModel
|
||||
*/
|
||||
$type = $this->rel('typeId');
|
||||
$type->loadById($data['typeId'], $accessToken, $additionalOptions);
|
||||
$data['typeId'] = $type;
|
||||
|
||||
$this->copyfrom($data, ['id', 'name', 'typeId', 'age', 'radius', 'temperature', 'luminosity', 'spectralClass']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,8 @@ class StructureModel extends BasicUniverseModel {
|
||||
'table' => 'type',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'x' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
|
||||
358
app/main/model/universe/systemmodel.php
Normal file
358
app/main/model/universe/systemmodel.php
Normal file
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 14.05.2018
|
||||
* Time: 19:29
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class SystemModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'system';
|
||||
|
||||
const ERROR_INVALID_WORMHOLE = 'Invalid wormhole name "%s" for system: "%s"';
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'constellationId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\ConstellationModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'constellation',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'starId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\StarModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'star',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'security' => [
|
||||
'type' => Schema::DT_VARCHAR128
|
||||
],
|
||||
'trueSec' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => false,
|
||||
'default' => 1
|
||||
],
|
||||
'securityStatus' => [
|
||||
'type' => Schema::DT_DOUBLE,
|
||||
'nullable' => false,
|
||||
'default' => 1
|
||||
],
|
||||
'securityClass' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
],
|
||||
'effect' => [
|
||||
'type' => Schema::DT_VARCHAR128
|
||||
],
|
||||
'shattered' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'x' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'y' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'z' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'planets' => [
|
||||
'has-many' => ['Model\Universe\PlanetModel', 'systemId']
|
||||
],
|
||||
'statics' => [
|
||||
'has-many' => ['Model\Universe\SystemStaticModel', 'systemId']
|
||||
],
|
||||
'stargates' => [
|
||||
'has-many' => ['Model\Universe\StargateModel', 'systemId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* get system data
|
||||
* -> this includes constellation, region, star, planets as well
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getData(){
|
||||
|
||||
$systemData = (object) [];
|
||||
$systemData->id = $this->_id;
|
||||
$systemData->name = $this->name;
|
||||
$systemData->constellation = $this->constellationId->getData();
|
||||
$systemData->star = $this->starId->getData();
|
||||
$systemData->security = $this->security;
|
||||
$systemData->trueSec = $this->trueSec;
|
||||
$systemData->effect = $this->effect;
|
||||
$systemData->shattered = $this->shattered;
|
||||
|
||||
if( !empty($planetsData = $this->getPlanetsData()) ){
|
||||
$systemData->planets = $planetsData;
|
||||
}
|
||||
|
||||
if( !empty($staticsData = $this->getStaticsData()) ){
|
||||
$systemData->statics = $staticsData;
|
||||
}
|
||||
|
||||
if( !empty($stargatesData = $this->getStargatesData()) ){
|
||||
$systemData->stargates = $stargatesData;
|
||||
}
|
||||
|
||||
return $systemData;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for row (un-formatted) trueSec
|
||||
* @param $secStatus
|
||||
* @return double
|
||||
*/
|
||||
public function set_securityStatus($secStatus){
|
||||
$secStatus = (double)$secStatus;
|
||||
// round for trueSec
|
||||
$positive = ($secStatus > 0);
|
||||
$trueSec = round($secStatus, 1, PHP_ROUND_HALF_DOWN);
|
||||
if($positive && $trueSec <= 0){
|
||||
$trueSec = 0.1;
|
||||
}
|
||||
$this->trueSec = $trueSec;
|
||||
// set 'security' for NON wormhole systems! -> those get updated from csv import
|
||||
if(!preg_match('/^j\d+$/i', $this->name)){
|
||||
if($trueSec <= 0){
|
||||
$security = '0.0';
|
||||
}elseif($trueSec < 0.5){
|
||||
$security = 'L';
|
||||
}else{
|
||||
$security = 'H';
|
||||
}
|
||||
$this->security = $security;
|
||||
}
|
||||
return $secStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for wormhole effect name
|
||||
* @param $effect
|
||||
* @return string|null
|
||||
*/
|
||||
public function set_effect($effect){
|
||||
$effect = (string)$effect;
|
||||
return $effect ? : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for positions array (x/y/z)
|
||||
* @param $position
|
||||
* @return null
|
||||
*/
|
||||
public function set_position($position){
|
||||
$position = (array)$position;
|
||||
if(count($position) === 3){
|
||||
$this->x = $position['x'];
|
||||
$this->y = $position['y'];
|
||||
$this->z = $position['z'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for static systems (wormholes)
|
||||
* -> comma separated string or array
|
||||
* @param $staticNames
|
||||
* @return null
|
||||
*/
|
||||
public function set_staticNames($staticNames){
|
||||
$staticNames = array_unique(is_string($staticNames) ? explode(',', $staticNames) : (array)$staticNames);
|
||||
$this->virtual('staticNames', array_map('strtoupper', $staticNames));
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event "Hook" function
|
||||
* return false will stop any further action
|
||||
* @param self $self
|
||||
* @param $pkeys
|
||||
* @throws \Exception\ValidationException
|
||||
*/
|
||||
public function afterUpdateEvent($self, $pkeys){
|
||||
$staticNames = (array)$self->staticNames;
|
||||
|
||||
if(
|
||||
count($staticNames) > 0 && // make sure statics are set. In case a wh system get updated without statics are set
|
||||
preg_match('/^c\d+$/i', $self->security) // make sure it is a wormhole
|
||||
){
|
||||
foreach((array)$self->statics as $static){
|
||||
if(in_array($static->wormholeId->name, $staticNames)){
|
||||
unset($staticNames[array_search($static->wormholeId->name, $staticNames)]);
|
||||
}else{
|
||||
$static->erase();
|
||||
}
|
||||
}
|
||||
|
||||
// add new statics
|
||||
foreach($staticNames as $staticName){
|
||||
$static = $self->rel('statics');
|
||||
/**
|
||||
* @var $wormhole WormholeModel
|
||||
*/
|
||||
$wormhole = $static->rel('wormholeId')->getByForeignKey('name', $staticName, ['limit' => 1]);
|
||||
if( !$wormhole->dry() ){
|
||||
$static->systemId = $self;
|
||||
$static->wormholeId = $wormhole;
|
||||
$static->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build search index
|
||||
$self->buildIndex();
|
||||
|
||||
return parent::afterUpdateEvent($self, $pkeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* get data from all planets
|
||||
* @return array
|
||||
*/
|
||||
protected function getPlanetsData(){
|
||||
$planetsData = [];
|
||||
|
||||
if($this->planets){
|
||||
foreach($this->planets as &$planet){
|
||||
/**
|
||||
* @var $planet PlanetModel
|
||||
*/
|
||||
$planetsData[] = $planet->getData();
|
||||
}
|
||||
}
|
||||
return $planetsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get data from all static wormholes
|
||||
* @return array
|
||||
*/
|
||||
protected function getStaticsData(){
|
||||
$staticsData = [];
|
||||
|
||||
if($this->statics){
|
||||
foreach($this->statics as &$static){
|
||||
/**
|
||||
* @var $static SystemStaticModel
|
||||
*/
|
||||
$staticsData[] = $static->getData();
|
||||
}
|
||||
}
|
||||
return $staticsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get data from all stargates
|
||||
* @return array
|
||||
*/
|
||||
protected function getStargatesData(){
|
||||
$stargatesData = [];
|
||||
|
||||
if($this->stargates){
|
||||
foreach($this->stargates as &$stargate){
|
||||
/**
|
||||
* @var $stargate StargateModel
|
||||
*/
|
||||
$stargatesData[] = $stargate->getData();
|
||||
}
|
||||
}
|
||||
return $stargatesData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $accessToken
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseSystemData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $constellation ConstellationModel
|
||||
*/
|
||||
$constellation = $this->rel('constellationId');
|
||||
$constellation->loadById($data['constellationId'], $accessToken, $additionalOptions);
|
||||
$data['constellationId'] = $constellation;
|
||||
|
||||
/**
|
||||
* @var $star StarModel
|
||||
*/
|
||||
$star = $this->rel('starId');
|
||||
$star->loadById($data['starId'], $accessToken, $additionalOptions);
|
||||
$data['starId'] = $star;
|
||||
|
||||
$this->copyfrom($data, ['id', 'name', 'constellationId', 'starId', 'securityStatus', 'securityClass', 'position']);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load planets data for this system
|
||||
*/
|
||||
public function loadPlanetsData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseSystemData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['planets'] as $planetData){
|
||||
/**
|
||||
* @var $planet PlanetModel
|
||||
*/
|
||||
$planet = $this->rel('planets');
|
||||
$planet->loadById($planetData->planet_id);
|
||||
$planet->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* load stargates for this system
|
||||
* -> stargates to destination system which is not in DB get ignored
|
||||
*/
|
||||
public function loadStargatesData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseSystemData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['stargates'] as $stargateId){
|
||||
/**
|
||||
* @var $stargate StargateModel
|
||||
*/
|
||||
$stargate = $this->rel('stargates');
|
||||
$stargate->loadById($stargateId);
|
||||
$stargate->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
app/main/model/universe/systemstaticmodel.php
Normal file
76
app/main/model/universe/systemstaticmodel.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 18.05.2018
|
||||
* Time: 17:50
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class SystemStaticModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'system_static';
|
||||
|
||||
protected $fieldConf = [
|
||||
'systemId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\SystemModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'system',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'wormholeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\WormholeModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'wormhole',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* No static columns added
|
||||
* @var bool
|
||||
*/
|
||||
protected $addStaticFields = false;
|
||||
|
||||
/**
|
||||
* get static data
|
||||
* @return null|string
|
||||
*/
|
||||
public function getData(){
|
||||
return $this->wormholeId ? $this->wormholeId->name : null;
|
||||
}
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){}
|
||||
|
||||
/**
|
||||
* overwrites parent
|
||||
* @param null $db
|
||||
* @param null $table
|
||||
* @param null $fields
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function setup($db=null, $table=null, $fields=null){
|
||||
$status = parent::setup($db,$table,$fields);
|
||||
|
||||
if($status === true){
|
||||
$status = parent::setMultiColumnIndex(['systemId', 'wormholeId'], true);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class TypeModel extends BasicUniverseModel {
|
||||
@@ -59,7 +58,8 @@ class TypeModel extends BasicUniverseModel {
|
||||
'table' => 'group',
|
||||
'on-delete' => 'CASCADE'
|
||||
]
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry',
|
||||
],
|
||||
'marketGroupId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
@@ -85,6 +85,15 @@ class TypeModel extends BasicUniverseModel {
|
||||
],
|
||||
'structures' => [
|
||||
'has-many' => ['Model\Universe\StructureModel', 'typeId']
|
||||
],
|
||||
'planets' => [
|
||||
'has-many' => ['Model\Universe\PlanetModel', 'typeId']
|
||||
],
|
||||
'stars' => [
|
||||
'has-many' => ['Model\Universe\StarModel', 'typeId']
|
||||
],
|
||||
'wormholes' => [
|
||||
'has-many' => ['Model\Universe\WormholeModel', 'typeId']
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
179
app/main/model/universe/wormholemodel.php
Normal file
179
app/main/model/universe/wormholemodel.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodu
|
||||
* Date: 13.05.2018
|
||||
* Time: 18:36
|
||||
*/
|
||||
|
||||
namespace Model\Universe;
|
||||
|
||||
use DB\SQL\Schema;
|
||||
|
||||
class WormholeModel extends BasicUniverseModel {
|
||||
|
||||
protected $table = 'wormhole';
|
||||
|
||||
public static $enableDataExport = true;
|
||||
public static $enableDataImport = true;
|
||||
|
||||
protected $fieldConf = [
|
||||
'name' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => '',
|
||||
'index' => true,
|
||||
'unique' => true
|
||||
],
|
||||
'typeId' => [
|
||||
'type' => Schema::DT_INT,
|
||||
'index' => true,
|
||||
'belongs-to-one' => 'Model\Universe\TypeModel',
|
||||
'constraint' => [
|
||||
[
|
||||
'table' => 'type',
|
||||
'on-delete' => 'SET NULL'
|
||||
]
|
||||
],
|
||||
'validate' => 'validate_notDry'
|
||||
],
|
||||
'static' => [
|
||||
'type' => Schema::DT_BOOL,
|
||||
'nullable' => false,
|
||||
'default' => 0
|
||||
],
|
||||
'security' => [
|
||||
'type' => Schema::DT_VARCHAR128,
|
||||
'nullable' => false,
|
||||
'default' => ''
|
||||
],
|
||||
'massTotal' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'massIndividual' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'massRegeneration' => [
|
||||
'type' => Schema::DT_BIGINT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'maxStableTime' => [
|
||||
'type' => Schema::DT_TINYINT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'signatureStrength' => [
|
||||
'type' => Schema::DT_FLOAT,
|
||||
'nullable' => true,
|
||||
'default' => null
|
||||
],
|
||||
'systems' => [
|
||||
'has-many' => ['Model\Universe\SystemStaticModel', 'wormholeId']
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* setter for typeId
|
||||
* @param string $typeId
|
||||
* @return string|int|null
|
||||
*/
|
||||
public function set_typeId($typeId){
|
||||
if(!is_object($typeId)){
|
||||
/**
|
||||
* @var $type TypeModel
|
||||
*/
|
||||
$type = $this->rel('typeId');
|
||||
$type->loadById((int)$typeId);
|
||||
$typeId = $type->dry() ? null : $type->_id;
|
||||
}
|
||||
return $typeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for massTotal
|
||||
* @param string $mass
|
||||
* @return int|null
|
||||
*/
|
||||
public function set_massTotal($mass){
|
||||
$mass = (int)$mass;
|
||||
return $mass ? : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for massIndividual
|
||||
* @param string $mass
|
||||
* @return string|null
|
||||
*/
|
||||
public function set_massIndividual($mass){
|
||||
$mass = (int)$mass;
|
||||
return $mass ? : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for massRegeneration
|
||||
* @param $mass
|
||||
* @return int|null
|
||||
*/
|
||||
public function set_massRegeneration($mass){
|
||||
$mass = (int)$mass;
|
||||
return $mass ? : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for maxStableTime
|
||||
* @param string $hours
|
||||
* @return int|null
|
||||
*/
|
||||
public function set_maxStableTime($hours){
|
||||
$hours = (int)$hours;
|
||||
return $hours ? : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* setter for signatureStrength
|
||||
* @param string $strength
|
||||
* @return float|null
|
||||
*/
|
||||
public function set_signatureStrength($strength){
|
||||
$strength = (float)$strength;
|
||||
return $strength ? : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $fields
|
||||
* @return bool
|
||||
*/
|
||||
public function exportData(array $fields = [
|
||||
'id', 'name', 'typeId', 'static', 'security', 'massTotal', 'massIndividual',
|
||||
'massRegeneration', 'maxStableTime', 'signatureStrength']
|
||||
){
|
||||
return parent::exportData($fields);
|
||||
}
|
||||
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
var_dump('loadData');
|
||||
var_dump($id);
|
||||
/*
|
||||
$data = self::getF3()->ccpClient->getUniverseTypesData($id, $additionalOptions);
|
||||
if(!empty($data)){
|
||||
$group = $this->rel('groupId');
|
||||
$group->loadById($data['groupId'], $accessToken, $additionalOptions);
|
||||
$data['groupId'] = $group;
|
||||
|
||||
$this->copyfrom($data);
|
||||
$this->save();
|
||||
} */
|
||||
}
|
||||
|
||||
protected function loadDataByKey(string $key, $value){
|
||||
var_dump('loadData');
|
||||
var_dump($key);
|
||||
var_dump($value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -72,3 +72,6 @@ FOREIGN_KEY_CHECKS = ON
|
||||
NODE = 6.0
|
||||
NPM = 3.10.0
|
||||
|
||||
[REQUIREMENTS.DATA]
|
||||
STRUCTURES = 33
|
||||
SHIPS = 490
|
||||
|
||||
@@ -14,24 +14,71 @@ define([
|
||||
splashOverlayClass: 'pf-splash' // class for "splash" overlay
|
||||
};
|
||||
|
||||
/**
|
||||
* send ajax request for index build
|
||||
* @param url
|
||||
* @param requestData
|
||||
* @param context
|
||||
* @param callback
|
||||
*/
|
||||
let sendRequest = (url, requestData, context, callback) => {
|
||||
if(requestData.count === 0){
|
||||
// first iteration
|
||||
context.target.button('loading');
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: requestData,
|
||||
context: context
|
||||
}).done(function(data){
|
||||
callback(this, data);
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': Failed. Please retry', text: reason, type: 'warning'});
|
||||
this.target.button('reset');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* set page observer
|
||||
*/
|
||||
let setPageObserver = () => {
|
||||
let body = $('body');
|
||||
|
||||
// collapse ---------------------------------------
|
||||
// collapse ---------------------------------------------------------------------------------------------------
|
||||
body.find('[data-toggle="collapse"]').css({cursor: 'pointer'}).on('click', function(){
|
||||
$(this).find('.pf-animate-rotate').toggleClass('right');
|
||||
});
|
||||
|
||||
// buttons ----------------------------------------
|
||||
// buttons ----------------------------------------------------------------------------------------------------
|
||||
// exclude "download" && "navigation" buttons
|
||||
body.find('.btn').not('.navbar-fixed-bottom .btn').not('[href^="?export"]').on('click', function(e){
|
||||
body.find('.btn')
|
||||
.not('.navbar-fixed-bottom .btn')
|
||||
.not('[data-action="clearIndex"]')
|
||||
.not('[data-action="buildIndex"]')
|
||||
.not('[href^="?export"]').on('click', function(e){
|
||||
$('.' + config.splashOverlayClass).showSplashOverlay();
|
||||
});
|
||||
|
||||
// tooltips ---------------------------------------
|
||||
// build/clear index buttons ----------------------------------------------------------------------------------
|
||||
// clear index buttons ----------------------------------------------------------------------------------------
|
||||
body.find('.btn[data-action="buildIndex"], .btn[data-action="clearIndex"]').on('click', function(e){
|
||||
e.preventDefault();
|
||||
let element = $(this);
|
||||
let url = '/api/setup/' + element.attr('data-action');
|
||||
sendRequest(url, {
|
||||
type: element.attr('data-type'),
|
||||
count: 0
|
||||
}, {
|
||||
target: element,
|
||||
url: url
|
||||
}, updateIndexCount);
|
||||
});
|
||||
|
||||
// tooltips ---------------------------------------------------------------------------------------------------
|
||||
body.initTooltips();
|
||||
|
||||
// change url (remove logout parameter)
|
||||
@@ -40,6 +87,42 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* update data count label for "indexed data"
|
||||
* @param context
|
||||
* @param responseData
|
||||
*/
|
||||
let updateIndexCount = (context, responseData) => {
|
||||
let countElement = context.target.closest('.row').children().eq(1).find('kbd');
|
||||
countElement.text(responseData.countBuildAll + '/' + responseData.countAll);
|
||||
countElement.removeClass('txt-color-success txt-color-danger txt-color-warning');
|
||||
if(responseData.countBuildAll >=responseData.countAll){
|
||||
countElement.addClass('txt-color-success');
|
||||
}else if(responseData.countBuildAll > 0){
|
||||
countElement.addClass('txt-color-warning');
|
||||
}else{
|
||||
countElement.addClass('txt-color-danger');
|
||||
}
|
||||
|
||||
context.target.find('.btn-progress').html(' ' + responseData.progress + '%').css('width', responseData.progress + '%');
|
||||
|
||||
// send next chunk of rows -> import only
|
||||
if(
|
||||
context.target.attr('data-action') === 'buildIndex' &&
|
||||
responseData.countBuildAll < responseData.countAll
|
||||
){
|
||||
sendRequest(context.url, {
|
||||
type: responseData.type,
|
||||
count: responseData.count
|
||||
}, {
|
||||
target: context.target,
|
||||
url: context.url
|
||||
}, updateIndexCount);
|
||||
}else{
|
||||
context.target.button('reset');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* perform a basic check if Clients (browser) can connect to the webSocket server
|
||||
*/
|
||||
@@ -165,10 +248,10 @@ define([
|
||||
*/
|
||||
$(function(){
|
||||
|
||||
// show app information in browser console --------
|
||||
// show app information in browser console --------------------------------------------------------------------
|
||||
Util.showVersionInfo();
|
||||
|
||||
// hide splash loading animation ------------------
|
||||
// hide splash loading animation ------------------------------------------------------------------------------
|
||||
$('.' + config.splashOverlayClass).hideSplashOverlay();
|
||||
|
||||
setPageObserver();
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -101,7 +101,7 @@ define(['jquery'], function($) {
|
||||
4: 'M609 - C4',
|
||||
5: 'L614 - C5',
|
||||
6: 'S804 - C6',
|
||||
7: 'F135 - Thera'
|
||||
7: 'F353 - Thera'
|
||||
},
|
||||
6: { // ORE
|
||||
1: 'Ordinary Perimeter Deposit', //*
|
||||
|
||||
@@ -14,24 +14,71 @@ define([
|
||||
splashOverlayClass: 'pf-splash' // class for "splash" overlay
|
||||
};
|
||||
|
||||
/**
|
||||
* send ajax request for index build
|
||||
* @param url
|
||||
* @param requestData
|
||||
* @param context
|
||||
* @param callback
|
||||
*/
|
||||
let sendRequest = (url, requestData, context, callback) => {
|
||||
if(requestData.count === 0){
|
||||
// first iteration
|
||||
context.target.button('loading');
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: requestData,
|
||||
context: context
|
||||
}).done(function(data){
|
||||
callback(this, data);
|
||||
}).fail(function( jqXHR, status, error) {
|
||||
let reason = status + ' ' + error;
|
||||
Util.showNotify({title: jqXHR.status + ': Failed. Please retry', text: reason, type: 'warning'});
|
||||
this.target.button('reset');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* set page observer
|
||||
*/
|
||||
let setPageObserver = () => {
|
||||
let body = $('body');
|
||||
|
||||
// collapse ---------------------------------------
|
||||
// collapse ---------------------------------------------------------------------------------------------------
|
||||
body.find('[data-toggle="collapse"]').css({cursor: 'pointer'}).on('click', function(){
|
||||
$(this).find('.pf-animate-rotate').toggleClass('right');
|
||||
});
|
||||
|
||||
// buttons ----------------------------------------
|
||||
// buttons ----------------------------------------------------------------------------------------------------
|
||||
// exclude "download" && "navigation" buttons
|
||||
body.find('.btn').not('.navbar-fixed-bottom .btn').not('[href^="?export"]').on('click', function(e){
|
||||
body.find('.btn')
|
||||
.not('.navbar-fixed-bottom .btn')
|
||||
.not('[data-action="clearIndex"]')
|
||||
.not('[data-action="buildIndex"]')
|
||||
.not('[href^="?export"]').on('click', function(e){
|
||||
$('.' + config.splashOverlayClass).showSplashOverlay();
|
||||
});
|
||||
|
||||
// tooltips ---------------------------------------
|
||||
// build/clear index buttons ----------------------------------------------------------------------------------
|
||||
// clear index buttons ----------------------------------------------------------------------------------------
|
||||
body.find('.btn[data-action="buildIndex"], .btn[data-action="clearIndex"]').on('click', function(e){
|
||||
e.preventDefault();
|
||||
let element = $(this);
|
||||
let url = '/api/setup/' + element.attr('data-action');
|
||||
sendRequest(url, {
|
||||
type: element.attr('data-type'),
|
||||
count: 0
|
||||
}, {
|
||||
target: element,
|
||||
url: url
|
||||
}, updateIndexCount);
|
||||
});
|
||||
|
||||
// tooltips ---------------------------------------------------------------------------------------------------
|
||||
body.initTooltips();
|
||||
|
||||
// change url (remove logout parameter)
|
||||
@@ -40,6 +87,42 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* update data count label for "indexed data"
|
||||
* @param context
|
||||
* @param responseData
|
||||
*/
|
||||
let updateIndexCount = (context, responseData) => {
|
||||
let countElement = context.target.closest('.row').children().eq(1).find('kbd');
|
||||
countElement.text(responseData.countBuildAll + '/' + responseData.countAll);
|
||||
countElement.removeClass('txt-color-success txt-color-danger txt-color-warning');
|
||||
if(responseData.countBuildAll >=responseData.countAll){
|
||||
countElement.addClass('txt-color-success');
|
||||
}else if(responseData.countBuildAll > 0){
|
||||
countElement.addClass('txt-color-warning');
|
||||
}else{
|
||||
countElement.addClass('txt-color-danger');
|
||||
}
|
||||
|
||||
context.target.find('.btn-progress').html(' ' + responseData.progress + '%').css('width', responseData.progress + '%');
|
||||
|
||||
// send next chunk of rows -> import only
|
||||
if(
|
||||
context.target.attr('data-action') === 'buildIndex' &&
|
||||
responseData.countBuildAll < responseData.countAll
|
||||
){
|
||||
sendRequest(context.url, {
|
||||
type: responseData.type,
|
||||
count: responseData.count
|
||||
}, {
|
||||
target: context.target,
|
||||
url: context.url
|
||||
}, updateIndexCount);
|
||||
}else{
|
||||
context.target.button('reset');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* perform a basic check if Clients (browser) can connect to the webSocket server
|
||||
*/
|
||||
@@ -165,10 +248,10 @@ define([
|
||||
*/
|
||||
$(function(){
|
||||
|
||||
// show app information in browser console --------
|
||||
// show app information in browser console --------------------------------------------------------------------
|
||||
Util.showVersionInfo();
|
||||
|
||||
// hide splash loading animation ------------------
|
||||
// hide splash loading animation ------------------------------------------------------------------------------
|
||||
$('.' + config.splashOverlayClass).hideSplashOverlay();
|
||||
|
||||
setPageObserver();
|
||||
|
||||
@@ -591,7 +591,7 @@
|
||||
<tr>
|
||||
<check if="{{ @tableData.fieldConf }}">
|
||||
<true>
|
||||
<td class="text-center" data-target=".{{ @tableName }}_col" data-toggle="collapse" aria-expanded="false" aria-controls="{{ @tableName }}_col">
|
||||
<td class="text-center" data-target=".{{ @dbInformation.info.name }}_{{ @tableName }}_col" data-toggle="collapse" aria-expanded="false" aria-controls="{{ @dbInformation.info.name }}_{{ @tableName }}_col">
|
||||
<i class="fas fa-fw fa-chevron-right pf-animate-rotate"></i>
|
||||
</td>
|
||||
</true>
|
||||
@@ -613,12 +613,12 @@
|
||||
</td>
|
||||
<td></td>
|
||||
<td class="text-center">
|
||||
<check if="{{ @tableData.empty === true }}">
|
||||
<check if="{{ @tableData.rows <= 0 }}">
|
||||
<true>
|
||||
<i class="fas fa-fw fa-battery-empty txt-color txt-color-warning" title="table empty"></i>
|
||||
</true>
|
||||
<false>
|
||||
<i class="fas fa-fw fa-battery-full" title="table filled"></i>
|
||||
<i class="fas fa-fw fa-battery-full" title="{{ @tableData.rows }} rows"></i>
|
||||
</false>
|
||||
</check>
|
||||
</td>
|
||||
@@ -638,7 +638,7 @@
|
||||
|
||||
{* Show Columns *}
|
||||
<check if="{{ @tableData.fieldConf }}">
|
||||
<tr class="{{ @tableName }}_col collapse">
|
||||
<tr class="{{ @dbInformation.info.name }}_{{ @tableName }}_col collapse">
|
||||
<td class="text-center bg-color bg-color-tealDarkest">
|
||||
<i class="fas fa-fw fa-hashtag"></i>
|
||||
</td>
|
||||
@@ -652,7 +652,7 @@
|
||||
</tr>
|
||||
<repeat group="{{ @tableData.fieldConf }}" key="{{ @columnName }}" value="{{ @columnData }}" counter="{{ @countCol }}">
|
||||
<check if="{{ @columnData.type }}">
|
||||
<tr class="{{ @tableName }}_col collapse">
|
||||
<tr class="{{ @dbInformation.info.name }}_{{ @tableName }}_col collapse">
|
||||
<td class="text-right bg-color bg-color-tealDarker">{{@countCol}}.</td>
|
||||
<td class="bg-color bg-color-tealDarker">{{ @columnName }}</td>
|
||||
<td class="bg-color bg-color-tealDarker text-center">
|
||||
@@ -750,7 +750,7 @@
|
||||
|
||||
{* Show Foreign Keys *}
|
||||
<check if="{{ @tableData.foreignKeys }}">
|
||||
<tr class="{{ @tableName }}_col collapse">
|
||||
<tr class="{{ @dbInformation.info.name }}_{{ @tableName }}_col collapse">
|
||||
<td class="text-center bg-color bg-color-tealDarkest">
|
||||
<i class="fas fa-fw fa-hashtag"></i>
|
||||
</td>
|
||||
@@ -763,7 +763,7 @@
|
||||
<td class="bg-color bg-color-tealDarkest"></td>
|
||||
</tr>
|
||||
<repeat group="{{ @tableData.foreignKeys }}" value="{{ @keyData }}" counter="{{ @countForeignKey }}">
|
||||
<tr class="{{ @tableName }}_col collapse">
|
||||
<tr class="{{ @dbInformation.info.name }}_{{ @tableName }}_col collapse">
|
||||
<td class="text-center bg-color bg-color-tealDarker">{{@countForeignKey}}.</td>
|
||||
<td class="bg-color bg-color-tealDarker" colspan="6">{{ @keyData.keyName }}</td>
|
||||
<td class="bg-color bg-color-tealDarker text-center">
|
||||
@@ -921,29 +921,33 @@
|
||||
</div>
|
||||
|
||||
<div class="panel-body no-padding text-left">
|
||||
<repeat group="{{ @indexInformation }}" key="{{ @model }}" value="{{ @indexData }}">
|
||||
<repeat group="{{ @indexInformation }}" key="{{ @type }}" value="{{ @indexData }}">
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<span class="btn disabled btn-fake">
|
||||
{{ @indexData.table }}
|
||||
|
||||
</span>
|
||||
<span class="btn disabled btn-fake pull-right">
|
||||
<check if="{{ @indexData.count }}">
|
||||
<true>
|
||||
<kbd class="txt-color txt-color-success">{{ @indexData.count }} rows</kbd>
|
||||
</true>
|
||||
<false>
|
||||
<kbd class="txt-color txt-color-danger">0 rows</kbd>
|
||||
</false>
|
||||
</check>
|
||||
</span>
|
||||
<div class="col-xs-4">
|
||||
<span class="btn disabled btn-fake">{{ @indexData.label }}</span>
|
||||
</div>
|
||||
<div class="col-xs-3 text-right">
|
||||
<check if="{{ @indexData.countBuild >= @indexData.countAll }}">
|
||||
<kbd class="txt-color txt-color-success">{{ @indexData.countBuild }}/{{ @indexData.countAll }}</kbd>
|
||||
</check>
|
||||
<check if="{{ @indexData.countBuild > 0 && @indexData.countBuild < @indexData.countAll}}">
|
||||
<kbd class="txt-color txt-color-warning">{{ @indexData.countBuild }}/{{ @indexData.countAll }}</kbd>
|
||||
</check>
|
||||
<check if="{{ @indexData.countBuild <= 0 }}">
|
||||
<kbd class="txt-color txt-color-danger">{{ @indexData.countBuild }}/{{ @indexData.countAll }}</kbd>
|
||||
</check>
|
||||
<check if="{{ @indexData.tooltip }}">
|
||||
<i class="fas fa-fw fa-sm fa-question-circle pf-help-light" title="{{ @indexData.tooltip }}"></i>
|
||||
</check>
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<div class="btn-group btn-group-justified">
|
||||
<repeat group="{{ @indexData.task }}" key="{{ @taskKey }}" value="{{ @taskData }}">
|
||||
<a href="?action={{ @taskData.action }}&model={{ @model }}#pf-setup-administration" class="btn {{ @taskData.btn }} {{ @taskData.btn=='btn-default' && !@indexData.count ?'disabled':'' }} {{ @dbWarnings ?'disabled':'' }}" role="button">
|
||||
<i class="fas fa-fw {{ @taskData.icon }}"></i> {{ @taskData.label }}
|
||||
<a href="?action={{ @taskData.action }}&model={{ @type }}#pf-setup-administration"
|
||||
data-type="{{ @type }}" data-action="{{ @taskData.action }}"
|
||||
data-loading-text="<i class='fas fa-sync fa-spin'></i> wait…<div class='btn-progress'></div>" autocomplete="off"
|
||||
class="btn {{ @taskData.btn }} {{ @taskData.btn=='btn-default' && !@indexData.countBuild ?'disabled':'' }} {{ @dbWarnings ?'disabled':'' }}" role="button">
|
||||
<i class="fas {{ @taskData.icon }}"></i> {{ @taskData.label }}
|
||||
</a>
|
||||
</repeat>
|
||||
</div>
|
||||
@@ -980,7 +984,7 @@
|
||||
<span class="btn btn-default disabled btn-fake">
|
||||
Redis cache
|
||||
</span>
|
||||
<a href="?action=clearCache#pf-setup-administration" class="btn btn-warning" role="button">
|
||||
<a href="?action=clearCache#pf-setup-administration" class="btn btn-danger" role="button">
|
||||
<i class="fas fa-fw fa-times"></i> Clear all keys
|
||||
</a>
|
||||
</div>
|
||||
@@ -1000,7 +1004,7 @@
|
||||
</check>
|
||||
</kbd>
|
||||
</span>
|
||||
<a href="?action=clearCache#pf-setup-administration" class="btn btn-warning {{ (@cacheSize.data) ?'':'disabled' }}" role="button">
|
||||
<a href="?action=clearCache#pf-setup-administration" class="btn btn-danger {{ (@cacheSize.data) ?'':'disabled' }}" role="button">
|
||||
<i class="fas fa-fw fa-times"></i> Delete files
|
||||
</a>
|
||||
</div>
|
||||
@@ -1037,7 +1041,7 @@
|
||||
<div class="panel-body no-padding">
|
||||
<div class="btn-group btn-group-justified">
|
||||
<span class="btn btn-default disabled btn-fake">Invalidate all Cookie data</span>
|
||||
<a href="?action=invalidateCookies#pf-setup-administration" class="btn btn-warning" role="button">
|
||||
<a href="?action=invalidateCookies#pf-setup-administration" class="btn btn-danger" role="button">
|
||||
<i class="fas fa-fw fa-times"></i> Clear authentication data
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -68,7 +68,7 @@ fieldset[disabled]{
|
||||
}
|
||||
|
||||
|
||||
// form fields with icons groups (stacked icons) ==================================================
|
||||
// form fields with icons groups (stacked icons) ======================================================================
|
||||
.input-icon-left:not(.input-icon-right){
|
||||
.fa-stack:first-child{
|
||||
left: 14px;
|
||||
@@ -105,16 +105,38 @@ fieldset[disabled]{
|
||||
}
|
||||
}
|
||||
|
||||
// "fake" button (no user interaction) ============================================================
|
||||
.btn-fake{
|
||||
border: none;
|
||||
text-align: left;
|
||||
cursor: default;
|
||||
opacity: 1 !important;
|
||||
color: $gray-light !important;
|
||||
background-color: $gray !important;
|
||||
|
||||
// buttons ============================================================================================================
|
||||
.btn{
|
||||
// "fake" button (no user interaction)
|
||||
&.btn-fake{
|
||||
border: none;
|
||||
text-align: left;
|
||||
cursor: default;
|
||||
opacity: 1 !important;
|
||||
color: $gray-light !important;
|
||||
background-color: $gray !important;
|
||||
}
|
||||
|
||||
// progress bar inside button
|
||||
.btn-progress{
|
||||
position: absolute;
|
||||
display: block;
|
||||
height: 100%;
|
||||
background-color: rgba($green, .2 );
|
||||
width: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
line-height: 30px;
|
||||
color: $orange-light;
|
||||
font-size: 10px;
|
||||
text-align: left;
|
||||
@include transition( width 0.1s linear);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// drag&drop zone
|
||||
.pf-form-dropzone{
|
||||
border: 2px dashed $gray-darker;
|
||||
|
||||
Reference in New Issue
Block a user