OBJ.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <?php
  2. /*
  3. * json files in schema/gui/core/
  4. * default_db-{tbl_name}.json - config for raw table - no 'parent_object' defined
  5. * {object_name}.json - config for object - require 'parent_object' (another object or default_db-* raw table object)
  6. # Instance table:
  7. `{tbl_name}__INSTANCE_CLOSURE`: `PRIMARY_KEY`, `INSTANCE`(przodek), `PARENT_INSTANCE`(przodek), `PARENT_DEPTH`(głębokość)
  8. TODO: closure tree http://dirtsimple.org/2010/11/simplest-way-to-do-tree-based-queries.html
  9. ## sql - fetch rows with instance:
  10. ```sql
  11. select t.*
  12. , (select inst.INSTANCE
  13. from {tbl_name}__INSTANCE_CLOSURE inst
  14. where inst.PRIMARY_KEY = t.{primary_key}
  15. order by inst.PARENT_DEPTH DESC limit 1) as _instance
  16. from {tbl_name} t
  17. ```
  18. ## sql - fetch all
  19. ```sql
  20. select t.*
  21. from {tbl_name} t
  22. right join {tbl_name}__INSTANCE_CLOSURE inst on(t.{primary_key} = inst.PRIMARY_KEY)
  23. ```
  24. # Ref tables:
  25. `{tbl_name}__REF__{target_table_name}`: `PRIMARY_KEY`, `REMOTE_PRIMARY_KEY`
  26. ## sql - make ref connection
  27. ```sql
  28. insert into `{tbl_name}__REF__{target_table_name}` (`PRIMARY_KEY`, `REMOTE_PRIMARY_KEY`)
  29. -- TODO: save action to _HIST table
  30. ```
  31. # Hist table:
  32. `{tbl_name}__ACTIVITY_LOG`:
  33. - `ID` int PRIMARY auto_increment - used in update to check conflicts
  34. - `PRIMARY_KEY` - same type as root table primary key
  35. - `ACTION_USER` varchar(20)
  36. - `ACTION_DATE` datetime
  37. - `ACTION_TYPE` enum('CREATE', 'UPDATE', 'DELETE')
  38. - `LOG` text - json with action details
  39. - `ERRORS` text - json with errors
  40. */
  41. class OBJ {
  42. public static function getName($json) {
  43. if (empty($json['name'])) throw new Exception("Struct error - name not defined in " . json_encode($json));
  44. return (is_array($json['name']))? end($json['name']) : $json['name'];
  45. }
  46. public static function getLabel($json) {
  47. if (empty($json['label'])) return null;
  48. return (is_array($json['label']))? end($json['label']) : $json['label'];
  49. }
  50. // @returns array of parent object names
  51. public static function getParentList($json) {
  52. if (empty($json['parent_object'])) return array();
  53. return (!is_array($json['parent_object']))? array($json['parent_object']) : array_reverse($json['parent_object']);
  54. }
  55. public static function getFields($json) {
  56. if (empty($json['fields'])) throw new Exception("Struct error - object has no fields");
  57. return $json['fields'];
  58. }
  59. public static function getTableFields($json) {
  60. if (empty($json['fields'])) throw new Exception("Struct error - object has no fields");
  61. $fields = array();
  62. foreach ($json['fields'] as $fldName => $fldConf) {
  63. switch ($fldConf['type']) {
  64. case 'int': $fields[] = $fldName; break;
  65. case 'string': $fields[] = $fldName; break;
  66. // TODO: more field types
  67. }
  68. }
  69. return $fields;
  70. }
  71. public static function getStorageName($json) {
  72. if (empty($json['db_source'])) throw new Exception("Struct error - object has no source defined");
  73. return $json['db_source'];
  74. }
  75. public static function getMainTableName($json) {
  76. if (empty($json['db_table'])) throw new Exception("Struct error - object has no table name defined");
  77. return $json['db_table'];
  78. }
  79. public static function getPrimaryKeyType($json) {
  80. // [keys] => Array(
  81. // [PRIMARY] => Array(
  82. // [fields] => ID
  83. // [auto_increment] => 1
  84. if (empty($json['keys'])) throw new Exception("Struct error - object has no keys defined");
  85. if (empty($json['keys']['PRIMARY'])) throw new Exception("Struct error - object has no PRIMARY key defined");
  86. if (empty($json['keys']['PRIMARY']['fields'])) throw new Exception("Struct error - object PRIMARY key has no fields defined");
  87. if (is_array($json['keys']['PRIMARY']['fields'])) throw new Exception("Not implemented - multiple fields primary key");
  88. $primaryKey = $json['keys']['PRIMARY']['fields'];
  89. if (empty($json['fields'][$primaryKey])) throw new Exception("Struct error - object priary key field not defined in fields list");
  90. $type = V::get('type', '', $json['fields'][$primaryKey]);
  91. if (empty($type)) throw new Exception("Struct error - object primary field type not defined");
  92. if ('int' != $type) throw new Exception("Not implemented - primary key field type is no int");
  93. return $type;
  94. }
  95. public static function getCoreObjectFromFile($objectName) {
  96. $objectFileName = str_replace("/", '-', $objectName);
  97. $filePath = APP_PATH_SCHEMA . "/gui/core/{$objectFileName}.json";
  98. if (!file_exists($filePath)) throw new Exception("File not exists {$filePath}", 404);
  99. $json = file_get_contents($filePath);
  100. DBG::_('DBG', '>1', "{$objectName} filePath", $filePath, __CLASS__, __FUNCTION__, __LINE__);
  101. DBG::_('DBG', '>1', "{$objectName} file content", $json, __CLASS__, __FUNCTION__, __LINE__);
  102. $json = @json_decode($json, $assoc = true);
  103. DBG::_('DBG', '>1', "{$objectName} json_last_error()", json_last_error(), __CLASS__, __FUNCTION__, __LINE__);
  104. if (null == $json && 0 !== json_last_error()) throw new Exception("Parse json error for object '{$objectName}': " . json_last_error());
  105. if (empty($json['parent_object'])) return $json;
  106. $jsonParent = array();
  107. $parent = (is_array($json['parent_object']))? end($json['parent_object']) : $json['parent_object'];
  108. $jsonParent = self::getCoreObjectFromFile($parent);
  109. return array_merge_recursive($jsonParent, $json);
  110. }
  111. public static function getCoreObjectList() {
  112. $objectList = array();
  113. $files = glob(APP_PATH_SCHEMA . "/gui/core/*.json", GLOB_NOSORT);
  114. //DBG::_(true, true, "files", $files, __CLASS__, __FUNCTION__, __LINE__);
  115. foreach ($files as $filePath) {
  116. $fileName = basename($filePath);
  117. $objName = substr($fileName, 0, -5);// remove ext '.json'
  118. $objectList[] = $objName;
  119. }
  120. return $objectList;
  121. }
  122. public static function checkInstall($json) {
  123. self::checkInstanceTable($json);
  124. self::checkRefTables($json);
  125. self::checkActivityLogTable($json);
  126. }
  127. public static function checkInstanceTable($json) {
  128. $mainTableName = self::getMainTableName($json);
  129. $instanceTableName = "{$mainTableName}__INSTANCE_CLOSURE";
  130. $primaryKeyType = "int(11)";// TODO: fetch from main table - default int(11)
  131. $storageName = self::getStorageName($json);
  132. $storagePdo = DB::getStorage($storageName);
  133. try {
  134. $mainTableStruct = $storagePdo->getTableStruct($mainTableName);
  135. } catch (Exception $e) {
  136. throw $e;
  137. // TODO: if (404 != $e->getCode()) -> CREATE BASE TABLE -- mv to TODO: checkMainTableInstall($json)
  138. }
  139. try {
  140. $instanceTableStruct = DB::getStorage()->getTableStruct($instanceTableName);
  141. } catch (Exception $e) {
  142. if (404 != $e->getCode()) throw $e;
  143. $instanceTableStruct = null;
  144. }
  145. $sqlCreate = "
  146. CREATE TABLE IF NOT EXISTS `{$instanceTableName}` (
  147. `PRIMARY_KEY` {$primaryKeyType} NOT NULL,
  148. `INSTANCE` varchar(124) NOT NULL,
  149. `PARENT_INSTANCE` varchar(124) DEFAULT NULL,
  150. `PARENT_DEPTH` int(11) NOT NULL,
  151. KEY `PRIMARY_KEY` (`PRIMARY_KEY`)
  152. ) ENGINE=MyISAM DEFAULT CHARSET=latin2
  153. ";
  154. DBG::_('DBG', '>2', "mainTableStruct", $mainTableStruct, __CLASS__, __FUNCTION__, __LINE__);
  155. DBG::_('DBG', '>2', "instanceTableStruct", $instanceTableStruct, __CLASS__, __FUNCTION__, __LINE__);
  156. DBG::_('DBG', '>2', "sqlCreate", $sqlCreate, __CLASS__, __FUNCTION__, __LINE__);
  157. if (!$instanceTableStruct) {
  158. DB::getPDO()->exec($sqlCreate);
  159. }
  160. try {
  161. $instanceTableStruct = DB::getStorage()->getTableStruct($instanceTableName);
  162. } catch (Exception $e) {
  163. throw new Exception("Wystąpiły błędy podczas tworzenie tabeli instancji dla {$mainTableName}");
  164. }
  165. DBG::_('DBG', '>2', "OK instance table exists - instanceTableStruct", $instanceTableStruct, __CLASS__, __FUNCTION__, __LINE__);
  166. }
  167. public static function checkRefTables($json) {
  168. // TODO: should go recurse or create on demand? - create on demand
  169. $objectName = self::getName($json);
  170. $mainTableName = self::getMainTableName($json);
  171. $objectPrimaryKeyType = self::getPrimaryKeyType($json);
  172. if (empty($json['fields'])) return;
  173. $toCreateRefTables = array();// ref table name => target primary key type
  174. foreach ($json['fields'] as $fldName => $fld) {
  175. if ('ref' != V::get('type', '', $fld)) continue;
  176. // [ref_LOCAL_EMAIL] => Array(
  177. // [label] => Adresy email lokalnej poczty
  178. // [type] => ref
  179. // [ref_config] => Array(
  180. // [name] => user__ref__local_email
  181. // [target] => local_email
  182. $refConf = V::get('ref_config', '', $fld);
  183. if (!$refConf) throw new Exception("Struct error - ref config not defined for field {$objectName}/{$fldName}");
  184. $refTarget = V::get('target', '', $refConf);
  185. if (!$refConf) throw new Exception("Struct error - ref target not defined for field {$objectName}/{$fldName}");
  186. $jsonTarget = OBJ::getCoreObjectFromFile($refTarget);
  187. $targetMainTableName = self::getMainTableName($jsonTarget);
  188. $refTableName = "{$mainTableName}__REF__{$targetMainTableName}";
  189. $targetPrimaryKeyType = self::getPrimaryKeyType($jsonTarget);
  190. DBG::_('DBG', '>1', "create table `{$refTableName}` ( `PRIMARY_KEY` {$objectPrimaryKeyType}, `REMOTE_PRIMARY_KEY` {$targetPrimaryKeyType}) ", null, __CLASS__, __FUNCTION__, __LINE__);
  191. $toCreateRefTables[ $refTableName ] = $targetPrimaryKeyType;
  192. }
  193. foreach ($toCreateRefTables as $refTableName => $targetPrimaryKeyType) {
  194. // `{tbl_name}__REF__{target_table_name}`: `PRIMARY_KEY`, `REMOTE_PRIMARY_KEY`
  195. $sqlCreate = "
  196. CREATE TABLE IF NOT EXISTS `{$refTableName}` (
  197. `PRIMARY_KEY` {$objectPrimaryKeyType} NOT NULL,
  198. `REMOTE_PRIMARY_KEY` {$targetPrimaryKeyType} NOT NULL,
  199. KEY `PRIMARY_KEY` (`PRIMARY_KEY`),
  200. KEY `REMOTE_PRIMARY_KEY` (`REMOTE_PRIMARY_KEY`)
  201. ) ENGINE=MyISAM DEFAULT CHARSET=latin2
  202. ";
  203. DB::getPDO()->exec($sqlCreate);
  204. try {
  205. $refTableStruct = DB::getStorage()->getTableStruct($refTableName);
  206. } catch (Exception $e) {
  207. throw new Exception("Wystąpiły błędy podczas tworzenie tabeli ref {$refTableName}");
  208. }
  209. }
  210. }
  211. public static function checkActivityLogTable($json) {
  212. // `{tbl_name}__ACTIVITY_LOG`:
  213. // - `ID` int PRIMARY auto_increment - used in update to check conflicts
  214. // - `PRIMARY_KEY` - same type as root table primary key
  215. // - `ACTION_USER` varchar(20)
  216. // - `ACTION_DATE` datetime
  217. // - `ACTION_TYPE` enum('CREATE', 'UPDATE', 'DELETE')
  218. // - `LOG` text - json with action details
  219. // - `ERRORS` text - json with errors
  220. $mainTableName = self::getMainTableName($json);
  221. $histTableName = "{$mainTableName}__ACTIVITY_LOG";
  222. $primaryKeyType = self::getPrimaryKeyType($json);// default "int";
  223. $storageName = self::getStorageName($json);
  224. $storagePdo = DB::getStorage($storageName);
  225. $mainTableStruct = $storagePdo->getTableStruct($mainTableName);
  226. try {
  227. $histTableStruct = DB::getStorage()->getTableStruct($histTableName);
  228. } catch (Exception $e) {
  229. if (404 != $e->getCode()) throw $e;
  230. $histTableStruct = null;
  231. }
  232. $sqlCreate = "
  233. CREATE TABLE IF NOT EXISTS `{$histTableName}` (
  234. `ID` int(11) NOT NULL AUTO_INCREMENT,
  235. `PRIMARY_KEY` {$primaryKeyType} NOT NULL,
  236. `ACTION_USER` varchar(20) NOT NULL,
  237. `ACTION_DATE` datetime NOT NULL,
  238. `ACTION_TYPE` enum('CREATE','UPDATE','DELETE') NOT NULL,
  239. `LOG` text NOT NULL,
  240. `ERRORS` text NOT NULL,
  241. PRIMARY KEY (`ID`),
  242. KEY `PRIMARY_KEY` (`PRIMARY_KEY`)
  243. ) ENGINE=MyISAM DEFAULT CHARSET=latin2
  244. ";
  245. DBG::_('DBG', '>2', "mainTableStruct", $mainTableStruct, __CLASS__, __FUNCTION__, __LINE__);
  246. DBG::_('DBG', '>2', "instanceTableStruct", $histTableStruct, __CLASS__, __FUNCTION__, __LINE__);
  247. DBG::_('DBG', '>2', "sqlCreate", $sqlCreate, __CLASS__, __FUNCTION__, __LINE__);
  248. if (!$histTableStruct) {
  249. DB::getPDO()->exec($sqlCreate);
  250. }
  251. try {
  252. $histTableStruct = DB::getStorage()->getTableStruct($histTableName);
  253. } catch (Exception $e) {
  254. throw new Exception("Wystąpiły błędy podczas tworzenie tabeli hist dla {$mainTableName}");
  255. }
  256. DBG::_('DBG', '>2', "OK hist table exists - histTableStruct", $histTableStruct, __CLASS__, __FUNCTION__, __LINE__);
  257. }
  258. public static function parseAll() {
  259. DBG::activate();
  260. $mapInheritance = array();
  261. $objList = self::getCoreObjectList();
  262. foreach ($objList as $objName) {
  263. $json = self::getCoreObjectFromFile($objName);
  264. $parentList = OBJ::getParentList($json);
  265. DBG::_(true, true, "obj({$objName}) parentList:", $parentList, __CLASS__, __FUNCTION__, __LINE__);
  266. if (!empty($parentList)) {
  267. /*
  268. obj(stanowisko) parentList = Array(
  269. [0] => group
  270. [1] => zasob
  271. [2] => default_db/crm_lista_zasobow
  272. */
  273. $rootTable = array_pop($parentList);
  274. if (!array_key_exists($rootTable, $mapInheritance)) $mapInheritance[$rootTable] = array();
  275. array_unshift($parentList, $objName);
  276. foreach ($parentList as $parentName) {
  277. if (!in_array($parentName, $mapInheritance[$rootTable])) $mapInheritance[$rootTable][] = $parentName;
  278. }
  279. }
  280. DBG::_(true, true, "mapInheritance:", $mapInheritance, __CLASS__, __FUNCTION__, __LINE__);
  281. }
  282. }
  283. }