RefConfig.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. <?php
  2. Lib::loadClass('Type_Field');
  3. Lib::loadClass('Type_RefConfig');
  4. Lib::loadClass('Api_WfsNs');
  5. Lib::loadClass('ACL');
  6. /*
  7. RefStorage - for ref data manipulation
  8. RefConfig - for ref config
  9. `CRM_REF_CONFIG`:
  10. - `ID`: primaryKey (used to generate **Storage** name for data)
  11. - `ROOT_OBJECT_NS`: root object namespace
  12. - `CHILD_NAME`: child typeName (or alias name - not supported yet)
  13. - `CHILD_NS`: child namespace (required when use `CHILD_NAME` as alias)
  14. - `A_STATUS`: status
  15. - 'WAITING': not Active, 'NORMAL': Active, 'DELETED': removed
  16. - `VERSION`: **Storage** version
  17. - `A_LAST_ACTION_DATE`: last action timestamp
  18. - `SOURCE`: place where data is stored
  19. - 'table': table with data
  20. - 'view': view by flat relation cache
  21. - 'backRef': view on ref table with replaced fields
  22. **Storage** - table or view - place where data is stored
  23. RefConfig::isActive($objectNamespace, $childTypeName) // Ref is active when: CRM_REF_CONFIG.A_STATUS = NORMAL
  24. RefConfig::getRefTable($objectNamespace, $childTypeName) // ref table - TODO not needed?
  25. */
  26. class RefConfig {
  27. static $REF_TABLE_VERSION = 2;
  28. static $REF_DB_SCHEMA_VERSION = 2;
  29. static function isActive($objectNamespace, $childTypeName) {
  30. $refInfo = self::fetch($objectNamespace, $childTypeName);
  31. return ('NORMAL' === $refInfo->status);
  32. }
  33. static function _getDBVersion() {
  34. return (int)DB::getPDO()->fetchValue(" select `CONF_VAL` from `CRM_CONFIG` where `CONF_KEY` = 'RefConfig__version' ");
  35. }
  36. static function _updateDBVersion($version) {
  37. DB::getPDO()->execSql(" REPLACE INTO CRM_CONFIG (`CONF_KEY`,`CONF_VAL`) VALUES ('RefConfig__version', '{$version}') ");
  38. }
  39. static function _fixDatabaseSchema() {
  40. static $_REF_DB_SCHEMA_VERSION;
  41. DBG::log("_fixDatabaseSchema \$_REF_DB_SCHEMA_VERSION({$_REF_DB_SCHEMA_VERSION})");
  42. if (!$_REF_DB_SCHEMA_VERSION) {
  43. $_REF_DB_SCHEMA_VERSION = self::_getDBVersion();
  44. if (2 === self::$REF_DB_SCHEMA_VERSION && $_REF_DB_SCHEMA_VERSION < self::$REF_DB_SCHEMA_VERSION) {
  45. DB::getPDO()->execSql("
  46. CREATE TABLE IF NOT EXISTS `CRM_REF_CONFIG` (
  47. `ID` INT NOT NULL AUTO_INCREMENT
  48. , `ROOT_OBJECT_NS` VARCHAR(255) NOT NULL
  49. , `CHILD_NAME` VARCHAR(255) NOT NULL
  50. , `CHILD_NS` VARCHAR(255) NOT NULL
  51. , `A_STATUS` enum('WAITING', 'NORMAL', 'DELETED') NOT NULL DEFAULT 'WAITING'
  52. , `VERSION` int(11) NOT NULL DEFAULT 0
  53. , `A_LAST_ACTION_DATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
  54. , `SOURCE` enum('table', 'view', 'backRef') NOT NULL DEFAULT 'table'
  55. , PRIMARY KEY (`ID`)
  56. ) ENGINE = MyISAM DEFAULT CHARSET=latin2;
  57. ");
  58. try { // upgrade from version 0.1
  59. DB::getPDO()->execSql(" ALTER TABLE `CRM_REF_CONFIG` ADD `SOURCE` enum('table', 'view', 'backRef') not null default 'table' ");
  60. } catch (Exception $e) {
  61. DBG::log($e);
  62. }
  63. try { // upgrade from version 0.2
  64. DB::getPDO()->execSql(" ALTER TABLE `CRM_REF_CONFIG` CHANGE `SOURCE` `SOURCE` enum('table', 'view', 'backRef') not null default 'table' ");
  65. } catch (Exception $e) {
  66. DBG::log($e);
  67. }
  68. DB::getPDO()->execSql("
  69. update CRM_REF_CONFIG
  70. set CHILD_NS = REPLACE(REPLACE(CHILD_NS, '__x3A__', '/'), ':', '/')
  71. where CHILD_NS = CHILD_NAME
  72. ");
  73. self::_updateDBVersion(2);
  74. }
  75. }
  76. }
  77. /** static function fetch(Type_Namespace $rootObjectNamespace, Type_TypeName $childName, Type_Namespace $childNamespace = null): Type_RefConfig */
  78. static function fetch($rootObjectNamespace, $childName, $childNamespace = null) { // @returns Type_RefConfig
  79. self::_fixDatabaseSchema();
  80. $rootObjectNamespace = ACL::getBaseNamespace($rootObjectNamespace);
  81. if (!$childNamespace && false !== strpos($childName, '__x3A__') && false !== strpos($childName, ':')) $childNamespace = Api_WfsNs::namespaceFromTypeName($childName);
  82. if (!$childNamespace) {
  83. $rootAcl = ACL::getAclByNamespace($rootObjectNamespace);
  84. $childXsdType = $rootAcl->getXsdFieldType($childName);
  85. list($typePrefix, $childTypeName) = explode(':', $childXsdType, 2);
  86. $childNamespace = Api_WfsNs::namespaceFromTypeName($childTypeName);
  87. DBG::log(['$childXsdType' => $childXsdType, '$typePrefix' => $typePrefix, '$childNamespace' => $childNamespace], 'array', "DBG get ref table ...");
  88. switch ($typePrefix) {
  89. case 'ref_uri': $childAcl = ACL::getAclByNamespace($childNamespace); break;
  90. case 'ref': $childAcl = ACL::getAclByTypeName($childNamespace); break;
  91. default: throw new Exception("Expected ref type for field '{$childName}' in object '{$rootObjectNamespace}'");
  92. }
  93. }
  94. $refInfo = DB::getPDO()->fetchFirst("
  95. select c.ID, c.A_STATUS, c.VERSION, c.SOURCE
  96. from `CRM_REF_CONFIG` c
  97. where c.ROOT_OBJECT_NS = :ROOT_OBJECT_NS
  98. and c.CHILD_NAME = :CHILD_NAME
  99. and c.CHILD_NS = :CHILD_NS
  100. ", [
  101. ':ROOT_OBJECT_NS' => $rootObjectNamespace,
  102. ':CHILD_NAME' => $childName,
  103. ':CHILD_NS' => $childNamespace,
  104. ]);
  105. if (empty($refInfo)) {
  106. $refInfo = [ 'ID' => 0, 'A_STATUS' => 'WAITING', 'VERSION' => self::$REF_TABLE_VERSION, 'SOURCE' => 'table',
  107. 'ROOT_OBJECT_NS' => $rootObjectNamespace,
  108. 'CHILD_NAME' => $childName,
  109. 'CHILD_NS' => $childNamespace,
  110. ];
  111. $refInfo['ID'] = DB::getPDO()->insert("CRM_REF_CONFIG", [
  112. 'ROOT_OBJECT_NS' => $rootObjectNamespace,
  113. 'CHILD_NAME' => $childName,
  114. 'CHILD_NS' => $childNamespace,
  115. 'VERSION' => self::$REF_TABLE_VERSION
  116. ]);
  117. }
  118. if (!$refInfo['ID']) throw new Exception("Ref table not found in ref config table for field '{$childName}' in object '{$rootObjectNamespace}'");
  119. return Type_RefConfig::build($refInfo);
  120. }
  121. static function getChildRefFullList($namespace) {
  122. $namespace = ACL::getBaseNamespace($namespace);
  123. if (!$namespace) throw new Exception("Missing namespace");
  124. return array_map('Type_RefConfig::build', DB::getPDO()->fetchAll("
  125. select c.ID
  126. , c.A_STATUS
  127. , c.SOURCE
  128. , c.CHILD_NAME
  129. from CRM_REF_CONFIG c
  130. where c.ROOT_OBJECT_NS = :namespace
  131. -- and c.A_STATUS = 'NORMAL'
  132. ", [
  133. ':namespace' => $namespace,
  134. ]));
  135. }
  136. static function isRefField($objectNamespace, $fieldName) {
  137. return (false !== strpos($fieldName, ':'));
  138. }
  139. /*
  140. @param $appInfo = [
  141. [type] => ref:default_db__x3A__BI_audit_CEIDG:BI_audit_CEIDG
  142. [minOccurs] => 0
  143. [maxOccurs] => unbounded
  144. [restrictions] => []
  145. [appInfo] => []
  146. ]
  147. */
  148. static function needUpdate($objectNamespace, $childTypeName, Type_Field $newField, Type_Field $oldField = null) {
  149. DBG::log(['objectNamespace' => $objectNamespace, 'childTypeName' => $childTypeName, 'newField' => $newField, 'oldField' => $oldField], 'array', "RefConfig::needUpdate...");
  150. if (!$oldField) throw new Exception("Missig oldField in RefConfig::needUpdate({$objectNamespace}, {$childTypeName}, ...) - TODO: fetch from #acl cache");
  151. if (!($newField instanceof Type_Field_Ref)) return false;
  152. if (!($oldField instanceof Type_Field_Ref)) return false;
  153. $oldRefActive = self::isActive($objectNamespace, $childTypeName);
  154. if (!$oldRefActive) return false; // if old not installed / adtivated then just fix struct and install
  155. $newRefSource = $newField->source;
  156. $oldRefConf = self::fetch($objectNamespace, $childTypeName);
  157. $oldRefSource = $oldRefConf->source;
  158. if ($newRefSource !== $oldRefSource) DBG::log("RefConfig::needUpdate Change ref source from '{$oldRefSource}' to '{$newRefSource}'");
  159. if ($newRefSource !== $oldRefSource) return true;
  160. return false;
  161. }
  162. static function update($objectNamespace, $childTypeName, Type_Field $newField) {
  163. if (!($newField instanceof Type_Field_Ref)) return;
  164. // $oldRefActive = self::isActive($objectNamespace, $childTypeName);
  165. // if (!$oldRefActive) return; // if old not installed / adtivated then just fix struct and install
  166. $newRefSource = $newField->source;
  167. $refConfig = self::fetch($objectNamespace, $childTypeName);
  168. $oldRefSource = $refConfig->source;
  169. if ($newRefSource !== $oldRefSource) DBG::log("RefConfig::update Change ref source from '{$oldRefSource}' to '{$newRefSource}'");
  170. { // always update ref config at reinstall - drop / create ref tables (table or view)
  171. switch ($newRefSource) {
  172. case 'table': return self::installRefTable($objectNamespace, $childTypeName, $newField, $refConfig);
  173. case 'view': return self::installRefView($objectNamespace, $childTypeName, $newField, $refConfig);
  174. case 'backRef': return self::installBackRef($objectNamespace, $childTypeName, $newField, $refConfig);
  175. }
  176. }
  177. if ($newRefSource !== $oldRefSource) {
  178. if ('table' === $oldRefSource) {
  179. // TODO: check if table has data
  180. // TODO: if no data in table then DROP ?
  181. }
  182. throw new Exception("TODO: RefConfig::update Change ref source from '{$oldRefSource}' to '{$newRefSource}' in '{$objectNamespace}'-&gt;'{$childTypeName}'");
  183. }
  184. }
  185. static function createRefTable($objectNamespace, $childTypeName, Type_RefConfig $refConfig = null) {
  186. if (!$refConfig) $refConfig = self::fetch($objectNamespace, $childTypeName);
  187. $refTableName = "CRM__#REF_TABLE__{$refConfig->id}";
  188. DB::getPDO()->execSql("
  189. CREATE TABLE IF NOT EXISTS `{$refTableName}` (
  190. `PRIMARY_KEY` int(11) NOT NULL
  191. , `REMOTE_PRIMARY_KEY` int(11) NOT NULL
  192. , `REMOTE_TYPENAME` varchar(255) NOT NULL DEFAULT ''
  193. , `A_STATUS` enum('WAITING', 'NORMAL', 'DELETED') NOT NULL DEFAULT 'WAITING'
  194. , `TRANSACTION_ID` int(11) NOT NULL
  195. , `A_LAST_ACTION_DATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
  196. , KEY `PRIMARY_KEY` (`PRIMARY_KEY`)
  197. , KEY `REMOTE_PRIMARY_KEY` (`REMOTE_PRIMARY_KEY`)
  198. , KEY `TRANSACTION_ID` (`TRANSACTION_ID`)
  199. ) ENGINE=MyISAM DEFAULT CHARSET=latin2 COMMENT='{$objectNamespace} #REF {$childTypeName}';
  200. ");
  201. $affected = DB::getPDO()->update("CRM_REF_CONFIG", 'ID', $refConfig->id, [
  202. 'A_STATUS' => "NORMAL",
  203. // 'VERSION' => self::$REF_TABLE_VERSION
  204. ]);
  205. self::upgradeRefTableFrom1to2($refConfig);
  206. }
  207. static function upgradeRefTableFrom1to2(Type_RefConfig $refConfig) { // TODO: rm ACL::upgradeRefConfigFrom1to2
  208. if (1 == $refConfig->version) {
  209. if ('table' === $refConfig->source && 'NORMAL' == $refConfig->status) {
  210. $refTableName = "CRM__#REF_TABLE__{$refConfig->id}";
  211. try {
  212. DB::getPDO()->execSql(" CREATE INDEX `TRANSACTION_ID` ON `{$refTableName}` (`TRANSACTION_ID`) ");
  213. } catch (Exception $e) {
  214. DBG::log($e);
  215. }
  216. }
  217. $affected = DB::getPDO()->update("CRM_REF_CONFIG", 'ID', $refConfig->id, [
  218. 'VERSION' => 2
  219. ]);
  220. }
  221. // TODO: return array_merge($refConfig, [ 'VERSION' => 2 ]);
  222. }
  223. static function installRefTable($objectNamespace, $childTypeName, Type_Field $newField, Type_RefConfig $refConfig = null) {
  224. if (!$refConfig) $refConfig = self::fetch($objectNamespace, $childTypeName);
  225. self::createRefTable($objectNamespace, $childTypeName, $refConfig);
  226. $affected = DB::getPDO()->update("CRM_REF_CONFIG", 'ID', $refConfig->id, [
  227. 'SOURCE' => "table",
  228. 'A_STATUS' => "NORMAL",
  229. 'VERSION' => self::$REF_TABLE_VERSION,
  230. ]);
  231. }
  232. static function installRefView($objectNamespace, $childTypeName, Type_Field $typeField, Type_RefConfig $refConfig = null) {
  233. if (!$refConfig) $refConfig = self::fetch($objectNamespace, $childTypeName);
  234. $viewSelectSql = RefConfig::generateRefSelectSqlByFlatRelationCache($objectNamespace, $childTypeName, $typeField);
  235. $refTableName = "CRM__#REF_TABLE__{$refConfig->id}_VIEW";
  236. DB::getPDO()->execSql(" CREATE OR REPLACE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `{$refTableName}` AS {$viewSelectSql} ");
  237. $affected = DB::getPDO()->update("CRM_REF_CONFIG", 'ID', $refConfig->id, [
  238. 'SOURCE' => 'view',
  239. 'A_STATUS' => "NORMAL",
  240. ]);
  241. }
  242. static function installBackRef($objectNamespace, $childTypeName, Type_Field $newField, Type_RefConfig $refConfig = null) {
  243. $viewSelectSql = self::generateRefSelectSqlByBackRef($objectNamespace, $childTypeName);
  244. if (!$refConfig) $refConfig = self::fetch($objectNamespace, $childTypeName);
  245. $refTableName = "CRM__#REF_TABLE__{$refConfig->id}_VIEW";
  246. DB::getPDO()->execSql(" CREATE OR REPLACE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `{$refTableName}` AS {$viewSelectSql} ");
  247. $affected = DB::getPDO()->update("CRM_REF_CONFIG", 'ID', $refConfig->id, [
  248. 'SOURCE' => 'backRef',
  249. 'A_STATUS' => "NORMAL",
  250. 'VERSION' => self::$REF_TABLE_VERSION,
  251. ]);
  252. }
  253. static function generateRefSelectSqlByBackRef($rootObjectNamespace, $childName) {
  254. // generate view which is select from {replaced(pk, remote pk) on ref table from backRef}
  255. // {
  256. // DBG::nicePrint($refInfo, "\$refInfo");
  257. // DBG::nicePrint($rootObjectNamespace, "\$rootObjectNamespace");
  258. // DBG::nicePrint($childName, "\$childName");
  259. // DBG::nicePrint($childNamespace, "\$childNamespace");
  260. // $replacedObjNs = Api_WfsNs::namespaceFromTypeName($childName);
  261. // $replacedChildName = Api_WfsNs::typeName($rootObjectNamespace);
  262. // DBG::nicePrint($replacedObjNs, "\$replacedObjNs");
  263. // DBG::nicePrint($replacedChildName, "\$replacedChildName");
  264. // return ACL::getRefTable($replacedObjNs, $replacedChildName, 1);
  265. // throw new Exception("Not Implemented ref SOURCE = '{$refInfo['SOURCE']}'");
  266. // }
  267. $childNs = Api_WfsNs::namespaceFromTypeName($childName);
  268. $rootTypeName = Api_WfsNs::typeName($rootObjectNamespace);
  269. $backRefTable = ACL::getRefTable($childNs, $rootTypeName);
  270. DBG::nicePrint($backRefTable, "ACL::getRefTable({$childNs}, {$rootTypeName})");
  271. // TODO: check if ref_config is not backRef to avoid loop // $refInfo = self::getRefConfig($fieldNs, $item['typeName'], $item['typeName']);
  272. $lastActionDateField = "NULL"; // , IF(l.A_RECORD_UPDATE_DATE > r.A_RECORD_UPDATE_DATE, l.A_RECORD_UPDATE_DATE, r.A_RECORD_UPDATE_DATE) as A_LAST_ACTION_DATE
  273. $sql = "
  274. select backRef.REMOTE_PRIMARY_KEY as PRIMARY_KEY
  275. , backRef.PRIMARY_KEY as REMOTE_PRIMARY_KEY
  276. , backRef.REMOTE_TYPENAME as REMOTE_TYPENAME
  277. , backRef.A_STATUS as A_STATUS
  278. , 0 as TRANSACTION_ID
  279. , {$lastActionDateField} as A_LAST_ACTION_DATE
  280. from `{$backRefTable}` backRef
  281. ";
  282. DBG::log($sql, 'sql', "generateRefSelectSqlByBackRef");
  283. return $sql;
  284. }
  285. static function generateRefSelectSqlByFlatRelationCache($rootObjectNamespace, $childName, Type_Field $typeField) { // CRM_REF_CONFIG
  286. $appInfo = $typeField->appInfo;
  287. if (empty($appInfo)) throw new Exception("Empty app:info for field '{$rootObjectNamespace}/{$childName}'");
  288. DBG::log(['$appInfo'=>$appInfo, '$rootObjectNamespace'=>$rootObjectNamespace, '$childName'=>$childName], 'array', "\$appInfo");
  289. $rootAcl = ACL::getAclByNamespace($rootObjectNamespace);
  290. $childXsdType = $rootAcl->getXsdFieldType($childName);
  291. list($typePrefix, $childNamespace) = explode(':', $childXsdType, 2);
  292. switch ($typePrefix) {
  293. case 'ref_uri': $childAcl = ACL::getAclByNamespace($childNamespace); break;
  294. case 'ref': $childAcl = ACL::getAclByTypeName($childNamespace); break;
  295. default: throw new Exception("Expected ref type for field '{$childName}' in object '{$rootObjectNamespace}'");
  296. }
  297. $lastActionDateField = "NULL"; // , IF(l.A_RECORD_UPDATE_DATE > r.A_RECORD_UPDATE_DATE, l.A_RECORD_UPDATE_DATE, r.A_RECORD_UPDATE_DATE) as A_LAST_ACTION_DATE
  298. $rootPrimaryKeyField = $rootAcl->getPrimaryKeyField();
  299. $childPrimaryKeyField = $childAcl->getPrimaryKeyField();
  300. $rootTableName = $rootAcl->getRootTableName();
  301. $childTableName = $childAcl->getRootTableName();
  302. // '$appInfo' => [
  303. // 'flat_relation_cache' => [
  304. // 'source' => [
  305. // '@name' => 'ID',
  306. // '@xpath' => 'default_db__x3A__CRM_WSKAZNIK:CRM_WSKAZNIK/ID_PROCES',
  307. // ),
  308. // ),
  309. // ),
  310. // '$rootObjectNamespace' => 'default_db/CRM_PROCES/PROCES',
  311. // '$childName' => 'default_db__x3A__CRM_WSKAZNIK:CRM_WSKAZNIK',
  312. // '$appInfo' => [
  313. // 'flat_relation_cache' => [
  314. // 'source' => [
  315. // '@name' => 'ID',
  316. // '@xpath' => 'default_db__x3A__CRM_PROCES:PROCES/PARENT_ID',
  317. // ),
  318. // ),
  319. // ),
  320. // '$rootObjectNamespace' => 'default_db/CRM_PROCES/PROCES',
  321. // '$childName' => 'default_db__x3A__CRM_PROCES:PROCES',
  322. $appInfoRootFieldName = null;
  323. $appInfoChildFieldName = null;
  324. {
  325. if (empty($appInfo['flat_relation_cache']['source']['@name'])) throw new Exception("Missing flat_relation_cache/source/@name");
  326. if (empty($appInfo['flat_relation_cache']['source']['@xpath'])) throw new Exception("Missing flat_relation_cache/source/@xpath");
  327. $appInfoName = $appInfo['flat_relation_cache']['source']['@name'];
  328. $appInfoXpath = $appInfo['flat_relation_cache']['source']['@xpath'];
  329. // $rootNs = $rootAcl->getNamespace()
  330. if ("{$childName}/" === substr($appInfoXpath, 0, strlen("{$childName}/"))) {
  331. $appInfoRootFieldName = substr($appInfoXpath, strlen("{$childName}/"));
  332. $appInfoChildFieldName = $appInfoName;
  333. } else {
  334. throw new Exception("TODO parse flat_relation_cache");
  335. }
  336. }
  337. if (!$appInfoRootFieldName || !$appInfoChildFieldName) throw new Exception("Error Processing flat_relation_cache");
  338. $sqlWhereFromRestrictions = [];
  339. DBG::log(['root'=>$rootAcl->getFields(), 'child'=>$childAcl->getFields()], 'array', "rootAcl and childAcl fields - xsdRestrictions");
  340. if ($rootAcl instanceof AntAclBase && $childAcl instanceof AntAclBase) {
  341. $rootLocalFieldsWithRestrictions = array_filter($rootAcl->getFields(), function ($field) {
  342. if (!$field['isLocal']) return false;
  343. if (empty($field['xsdRestrictions'])) return false;
  344. if ('[]' == $field['xsdRestrictions']) return false;
  345. return true;
  346. });
  347. $childLocalFieldsWithRestrictions = array_filter($childAcl->getFields(), function ($field) {
  348. if (!$field['isLocal']) return false;
  349. if (empty($field['xsdRestrictions'])) return false;
  350. if ('[]' == $field['xsdRestrictions']) return false;
  351. return true;
  352. });
  353. DBG::log(['root'=>$rootLocalFieldsWithRestrictions, 'child'=>$childLocalFieldsWithRestrictions], 'array', "root and child fields with xsdRestrictions");
  354. if (!empty($rootLocalFieldsWithRestrictions)) {
  355. $sqlTablePrefix = 'root';
  356. $sqlWhereFromRestrictions = array_reduce(
  357. array_map(function ($field) use ($sqlTablePrefix) {
  358. $sqlRestrictions = [];
  359. // 'xsdRestrictions' => '{"enumeration":{"PROCES":"PROCES"}}',
  360. $restrictions = @json_decode($field['xsdRestrictions'], $assoc = true);
  361. if (!empty($restrictions)) {
  362. if (!empty($restrictions['enumeration'])) {
  363. $sqlRestrictions[] = "{$sqlTablePrefix}.`{$field['fieldNamespace']}` in (" . implode(",", array_map([DB::getPDO(), 'quote'], array_keys($restrictions['enumeration']))) . ")";
  364. }
  365. }
  366. return $sqlRestrictions;
  367. }, $rootLocalFieldsWithRestrictions),
  368. function ($ret, $cur) {
  369. return array_merge($ret, array_filter($cur, ['V', 'filterNotEmpty']));
  370. },
  371. $sqlWhereFromRestrictions
  372. );
  373. }
  374. if (!empty($childLocalFieldsWithRestrictions)) {
  375. $sqlTablePrefix = 'child';
  376. $sqlWhereFromRestrictions = array_reduce(
  377. array_map(function ($field) use ($sqlTablePrefix) {
  378. $sqlRestrictions = [];
  379. // 'xsdRestrictions' => '{"enumeration":{"PROCES":"PROCES"}}',
  380. $restrictions = @json_decode($field['xsdRestrictions'], $assoc = true);
  381. if (!empty($restrictions)) {
  382. if (!empty($restrictions['enumeration'])) {
  383. $sqlRestrictions[] = "{$sqlTablePrefix}.`{$field['fieldNamespace']}` in (" . implode(",", array_map([DB::getPDO(), 'quote'], array_keys($restrictions['enumeration']))) . ")";
  384. }
  385. }
  386. return $sqlRestrictions;
  387. }, $childLocalFieldsWithRestrictions),
  388. function ($ret, $cur) {
  389. return array_merge($ret, array_filter($cur, ['V', 'filterNotEmpty']));
  390. },
  391. $sqlWhereFromRestrictions
  392. );
  393. }
  394. }
  395. $sqlWhereFromRestrictions = (!empty($sqlWhereFromRestrictions)) ? implode(" and ", $sqlWhereFromRestrictions) : "1=1";
  396. $sqlChildFieldName = $childAcl->getSqlFieldName($appInfoRootFieldName);
  397. $sql = "
  398. select root.{$rootPrimaryKeyField} as PRIMARY_KEY
  399. , child.{$childPrimaryKeyField} as REMOTE_PRIMARY_KEY
  400. , '' as REMOTE_TYPENAME
  401. , 'WAITING' as A_STATUS
  402. , 0 as TRANSACTION_ID
  403. , {$lastActionDateField} as A_LAST_ACTION_DATE
  404. from `{$rootTableName}` root
  405. join `{$childTableName}` child on(child.{$sqlChildFieldName} = root.{$appInfoChildFieldName})
  406. where {$sqlWhereFromRestrictions}
  407. ";
  408. DBG::log($sql, 'sql', "generateRefSelectSqlByFlatRelationCache");
  409. return $sql;
  410. }
  411. static function remove(Type_RefConfig $refConfig) {
  412. DB::getPDO()->update('CRM_REF_CONFIG', 'ID', $refConfig->id, [
  413. 'A_STATUS' => 'DELETED',
  414. 'A_LAST_ACTION_DATE' => 'NOW()',
  415. ]);
  416. }
  417. static function reactivate(Type_RefConfig $refConfig) {
  418. DB::getPDO()->update('CRM_REF_CONFIG', 'ID', $refConfig->id, [ // TODO: update ref table, update source -- fixed below by RefConfig::update
  419. 'A_STATUS' => 'WAITING',
  420. 'A_LAST_ACTION_DATE' => 'NOW()',
  421. ]);
  422. }
  423. }