InstanceConfig.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. Lib::loadClass('SchemaVersionUpgrade');
  3. class InstanceConfig {
  4. static function createInstanceConfigTable() {
  5. DB::getPDO()->execSql("
  6. create table if not exists `CRM_INSTANCE_CONFIG` (
  7. `id` int(11) not null AUTO_INCREMENT,
  8. `namespace` varchar(255) NOT NULL DEFAULT '',
  9. `rootNamespace` varchar(255) NOT NULL DEFAULT '',
  10. `idInstanceBase` int(11) NOT NULL DEFAULT 0,
  11. `_createdAt` datetime NOT NULL,
  12. UNIQUE KEY `namespace` (`namespace`),
  13. KEY `rootNamespace` (`rootNamespace`),
  14. PRIMARY KEY (`id`)
  15. ) ENGINE=MyISAM DEFAULT CHARSET=latin2
  16. ");
  17. }
  18. static function getInstanceTable($namespace) { // @returns tableName with struct { pk, idInstance, _createdAt }
  19. $conf = self::getInstanceConfig($namespace);
  20. if (!empty($conf['idInstanceBase'])) return "CRM__#INSTANCE_TABLE__{$conf['idInstanceBase']}";
  21. $rootNs = $conf['rootNamespace'];
  22. $rootConf = self::getInstanceConfig($rootNs);
  23. $instanceTableName = "CRM__#INSTANCE_TABLE__{$rootConf['id']}";
  24. if (!empty($rootConf['idInstance'])) {
  25. $affected = DB::getPDO()->update("CRM_INSTANCE_CONFIG", 'rootNamespace', $rootNs, [
  26. 'idInstanceBase' => $rootConf['id']
  27. ]);
  28. return $instanceTableName;
  29. }
  30. // TODO: fetch primaryKeyType - TODO: store primaryKey and primaryKeyType in SystemObject item
  31. $pkType = 'int';
  32. DB::getPDO()->exec("
  33. CREATE TABLE IF NOT EXISTS `{$instanceTableName}` (
  34. `pk` int(11) NOT NULL COMMENT 'primary key'
  35. , `idInstance` int(11) NOT NULL
  36. , `_createdAt` datetime NOT NULL
  37. , KEY `pk` (`pk`)
  38. , KEY `idInstance` (`idInstance`)
  39. ) ENGINE=MyISAM DEFAULT CHARSET=latin2 COMMENT='{$rootNs} #INSTANCE';
  40. ");
  41. $affected = DB::getPDO()->update("CRM_INSTANCE_CONFIG", 'rootNamespace', $rootNs, [
  42. 'idInstanceBase' => $rootConf['id']
  43. ]);
  44. return $instanceTableName;
  45. }
  46. static function getInstanceId($namespace) {
  47. return self::getInstanceConfig($namespace)['id'];
  48. }
  49. static function getInstanceConfig($namespace) { // @returns { id, namespace, rootNamespace, idInstanceBase, _createdAt }
  50. SchemaVersionUpgrade::upgradeSchema();
  51. try {
  52. $conf = self::fetchInstanceConfig($namespace);
  53. } catch (Exception $e) {
  54. self::createInstanceConfigTable();
  55. // TODO:?: `_tableInstalled` tinyint(1) not null default 0,
  56. $conf = self::fetchInstanceConfig($namespace);
  57. }
  58. if (!$conf) {
  59. $id = DB::getPDO()->insert("CRM_INSTANCE_CONFIG", [
  60. 'namespace' => $namespace,
  61. 'rootNamespace' => self::getRootNamespace($namespace),
  62. '_createdAt' => 'NOW()',
  63. ]);
  64. $conf = self::fetchInstanceConfig($namespace);
  65. }
  66. if (!$conf) throw new Exception("Instance not found in config table '{$namespace}'");
  67. return $conf;
  68. }
  69. static function fetchInstanceConfig($namespace) {
  70. return DB::getPDO()->fetchFirst("
  71. select c.*
  72. from `CRM_INSTANCE_CONFIG` c
  73. where c.namespace = :namespace
  74. ", [
  75. ':namespace' => $namespace
  76. ]);
  77. }
  78. static function getRootNamespace($namespace) { // TODO: works only for relative urls! - mv to Acl->getRootNamespace
  79. Lib::loadClass('SchemaFactory');
  80. try {
  81. $objectItem = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace);
  82. } catch (Exception $e) {
  83. throw new Exception("Object not installed '{$namespace}'");
  84. }
  85. if (!$objectItem['isStructInstalled']) throw new Exception("Object structure not installed '{$namespace}'");
  86. if ($objectItem['idDatabase'] != DB::getPDO()->getZasobId()) {
  87. if ('StorageAcl' === $objectItem['_type']) {
  88. DBG::log("getRootNamespace...");
  89. return $objectItem['namespace'];
  90. }
  91. else {
  92. throw new Exception("Only default_db supported"); // TODO: support more Sources
  93. }
  94. }
  95. return "default_db/{$objectItem['_rootTableName']}";
  96. }
  97. static function getNamespaceSiblings($namespace) {
  98. return array_map(function ($row) {
  99. return $row['namespace'];
  100. }, DB::getPDO()->fetchAll("
  101. select s.namespace
  102. from CRM_INSTANCE_CONFIG c
  103. join CRM_INSTANCE_CONFIG s on ( s.rootNamespace = c.rootNamespace and s.namespace != c.rootNamespace )
  104. where c.namespace = :namespace
  105. ", [
  106. 'namespace' => $namespace
  107. ]));
  108. }
  109. static function getFeatureNamespaces($namespace, $pk) {
  110. $instanceTable = self::getInstanceTable($namespace);
  111. return array_map(function ($row) {
  112. return $row['namespace'];
  113. }, DB::getPDO()->fetchAll("
  114. select c.namespace
  115. from `{$instanceTable}` i
  116. join `CRM_INSTANCE_CONFIG` c on ( c.id = i.idInstance )
  117. where i.pk = :pk
  118. ", [
  119. 'pk' => $pk,
  120. ]));
  121. }
  122. static function generateSqlWhereFromFieldsWithRestrictions($fieldsRestrictions, $sqlTablePrefix = 't') { // @param $fieldsRestrictions Array [ fieldName => xsdRestrictions | Null ]
  123. // TODO: add @param $acl to check if field is local, and if not then generate remote query (join)
  124. if (empty($fieldsRestrictions)) return '';
  125. $fieldsWithRestrictions = array_filter($fieldsRestrictions, ['V', 'filterNotEmpty']);
  126. if (empty($fieldsWithRestrictions)) return '';
  127. return array_reduce(
  128. array_map(function ($fieldName, $xsdRestriction) use ($sqlTablePrefix) {
  129. $sqlRestrictions = [];
  130. if (false !== strpos($fieldName, ':')) return $sqlRestrictions; // SKIP ref fields - TODO: generate remote query, require $acl
  131. if (!empty($xsdRestriction)) {
  132. if (!empty($xsdRestriction['enumeration'])) {
  133. $sqlRestrictions[] = "{$sqlTablePrefix}.`{$fieldName}` in (" . implode(",", array_map([DB::getPDO(), 'quote'], array_keys($xsdRestriction['enumeration']))) . ")";
  134. }
  135. }
  136. return $sqlRestrictions;
  137. }, array_keys($fieldsWithRestrictions), array_values($fieldsWithRestrictions)),
  138. function ($ret, $cur) {
  139. return array_merge($ret, array_filter($cur, ['V', 'filterNotEmpty']));
  140. },
  141. []
  142. );
  143. }
  144. static function generateSqlWhereFromFieldRestrictions($fields, $sqlTablePrefix = 't') {
  145. return (!empty($fields))
  146. ? array_reduce(
  147. array_map(function ($field) use ($sqlTablePrefix) {
  148. $sqlRestrictions = [];
  149. // 'xsdRestrictions' => '{"enumeration":{"PROCES":"PROCES"}}',
  150. $restrictions = @json_decode($field['xsdRestrictions'], $assoc = true);
  151. if (!empty($restrictions)) {
  152. if (!empty($restrictions['enumeration'])) {
  153. $sqlRestrictions[] = "{$sqlTablePrefix}.`{$field['fieldNamespace']}` in (" . implode(",", array_map([DB::getPDO(), 'quote'], array_keys($restrictions['enumeration']))) . ")";
  154. }
  155. }
  156. return $sqlRestrictions;
  157. }, $fields),
  158. function ($ret, $cur) {
  159. return array_merge($ret, array_filter($cur, ['V', 'filterNotEmpty']));
  160. },
  161. []
  162. )
  163. : ''
  164. ;
  165. }
  166. static function generateIsInstanceFunctionBody($namespace, $item = null) {
  167. if (!$item) $item = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => '*,field' ]);
  168. if (!in_array( $item['_type'], [ 'AntAcl' ] )) return null;
  169. $localFieldsWithRestrictions = array_filter($item['field'], function ($field) {
  170. if (!$field['isLocal']) return false;
  171. if (empty($field['xsdRestrictions'])) return false;
  172. if ('[]' == $field['xsdRestrictions']) return false;
  173. return true;
  174. });
  175. // TODO: get fields with minOccurs > 1 (may require select by ref)
  176. $sqlTablePrefix = 'root';
  177. DBG::nicePrint($localFieldsWithRestrictions, "\$localFieldsWithRestrictions");
  178. $sqlWhereFromRestrictions = ACL::generateSqlWhereFromFieldRestrictions($localFieldsWithRestrictions, $sqlTablePrefix);
  179. DBG::nicePrint($sqlWhereFromRestrictions, "\$sqlWhereFromRestrictions");
  180. $sqlWhereFromRestrictions = (!empty($sqlWhereFromRestrictions)) ? implode(" and ", $sqlWhereFromRestrictions) : "1=1";
  181. $pkField = 'ID'; // TODO: primaryKeyField into SystemObject structure
  182. $rootTableName = $item['_rootTableName'];
  183. $sqlFunBody = ("1=1" !== $sqlWhereFromRestrictions)
  184. ? " RETURN IF(
  185. (select count(1) as cnt from `{$rootTableName}` root where root.`{$pkField}` = pk and {$sqlWhereFromRestrictions}) > 0
  186. , 1, 0)
  187. "
  188. : " RETURN 1; ";
  189. return $sqlFunBody;
  190. }
  191. static function generateIsInstanceView($namespace, $item = null) {
  192. if (!$item) $item = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => '*,field' ]);
  193. if (!in_array( $item['_type'], [ 'AntAcl' ] )) return null;
  194. $idInstance = self::getInstanceId($namespace);
  195. $localFieldsWithRestrictions = array_filter($item['field'], function ($field) {
  196. if (!$field['isLocal']) return false;
  197. if (empty($field['xsdRestrictions'])) return false;
  198. if ('[]' == $field['xsdRestrictions']) return false;
  199. return true;
  200. });
  201. // TODO: get fields with minOccurs > 1 (may require select by ref)
  202. $fieldsWithRestrictions = array_reduce($localFieldsWithRestrictions, function ($ret, $field) {
  203. $fieldName = $field['fieldNamespace'];
  204. $restrictions = @json_decode($field['xsdRestrictions'], $assoc = true);
  205. $ret[$fieldName] = (!empty($restrictions)) ? $restrictions : null;
  206. return $ret;
  207. }, []);
  208. $sqlTablePrefix = "instance_{$idInstance}";
  209. $sqlListWhere = self::generateSqlWhereFromFieldsWithRestrictions($fieldsWithRestrictions, $sqlTablePrefix);
  210. $sqlWhere = (!empty($sqlListWhere)) ? implode("\n\t and ", $sqlListWhere) : "1=1";
  211. $rootTableName = $item['_rootTableName'];
  212. $primaryKey = 'ID'; // TODO: primaryKeyField into SystemObject structure // TODO: is in struct, read from `primaryKey`? field
  213. return implode("\n\t", [
  214. "select {$primaryKey}",
  215. "from `{$rootTableName}` `{$sqlTablePrefix}`",
  216. "where {$sqlWhere}",
  217. ]);
  218. }
  219. }