浏览代码

added support for system_cache__appinfo:backref_evaluate

Piotr Labudda 8 年之前
父节点
当前提交
8548bcf73f
共有 4 个文件被更改,包括 173 次插入26 次删除
  1. 67 5
      SE/se-lib/ACL.php
  2. 77 6
      SE/se-lib/Route/Storage/AclReinstall.php
  3. 22 13
      SE/se-lib/Route/Storage/AclStruct.php
  4. 7 2
      SE/se-lib/XML.php

+ 67 - 5
SE/se-lib/ACL.php

@@ -219,6 +219,8 @@ class ACL {
 
 	public static function getRefTable($rootObjectNamespace, $childName) { // CRM_REF_CONFIG
 		static $cacheRefTables = array();
+		if ($recurseCounter > 1) throw new Exception("BUG loop in ref config (SOURCE = 'backRef') for ns({$rootObjectNamespace}) child({$childName})");
+
 		DBG::log("DBG get ref table ({$rootObjectNamespace}, {$childName}) ...");
 		$rootObjectNamespace = ACL::getBaseNamespace($rootObjectNamespace);
 		$cacheKey = "{$rootObjectNamespace}/{$childName}";
@@ -238,7 +240,9 @@ class ACL {
 
 		if ('view' === $refInfo['SOURCE']) {
 			$refTableName = "CRM__#REF_TABLE__{$refInfo['ID']}_VIEW"; // view created by ACL::generateRefSelectSqlByFlatRelationCache
-		} else {
+		} else if ('backRef' === $refInfo['SOURCE']) {
+			$refTableName = "CRM__#REF_TABLE__{$refInfo['ID']}_VIEW"; // view created by ACL::generateRefSelectSqlByFlatRelationCache
+		} else if ('table' === $refInfo['SOURCE']) {
 			$refTableName = "CRM__#REF_TABLE__{$refInfo['ID']}";
 			if ('WAITING' == $refInfo['A_STATUS']) {
 				DB::getPDO()->execSql("
@@ -260,6 +264,8 @@ class ACL {
 					'VERSION' => $refInfo['VERSION']
 				]);
 			}
+		} else {
+			throw new Exception("Not Implemented ref SOURCE = '{$refInfo['SOURCE']}'");
 		}
 
 		if ($refInfo['VERSION'] < self::$REF_TABLE_VERSION) throw new Exception("TODO: ref table {$refInfo['ID']} require upgrade - field '{$childName}' in object '{$rootObjectNamespace}'");
@@ -272,6 +278,45 @@ class ACL {
 		$refInfo = self::getRefConfig($rootObjectNamespace, $childName);
 		return V::get('SOURCE', 'table', $refInfo);
 	}
+	public static function decodeAppInfoJson($appInfoJsonString) {
+		$appInfo = @json_decode($appInfoJsonString, $assoc = true);
+		if (null == $appInfo && 0 !== json_last_error()) throw new Exception("Parsing Json failed: " . json_last_error());
+		return $appInfo;
+	}
+	public static function generateRefSelectSqlByBackRef($rootObjectNamespace, $childName) { // TODO: mv to generateRefSelectSqlByFlatRelationCache
+		// TODO: generate view which is select from {replaced(pk, remote pk) on ref table from backRef}
+		// {
+		// 	DBG::nicePrint($refInfo, "\$refInfo");
+		// 	DBG::nicePrint($rootObjectNamespace, "\$rootObjectNamespace");
+		// 	DBG::nicePrint($childName, "\$childName");
+		// 	DBG::nicePrint($childNamespace, "\$childNamespace");
+		// 	$replacedObjNs = Api_WfsNs::namespaceFromTypeName($childName);
+		// 	$replacedChildName = Api_WfsNs::typeName($rootObjectNamespace);
+		// 	DBG::nicePrint($replacedObjNs, "\$replacedObjNs");
+		// 	DBG::nicePrint($replacedChildName, "\$replacedChildName");
+		// 	return ACL::getRefTable($replacedObjNs, $replacedChildName, 1);
+		// 	throw new Exception("Not Implemented ref SOURCE = '{$refInfo['SOURCE']}'");
+		// }
+		$childNs = Api_WfsNs::namespaceFromTypeName($childName);
+		$rootTypeName = Api_WfsNs::typeName($rootObjectNamespace);
+		$backRefTable = ACL::getRefTable($childNs, $rootTypeName);
+		DBG::nicePrint($backRefTable, "ACL::getRefTable({$childNs}, {$rootTypeName})");
+
+		// TODO: check if ref_config is not backRef to avoid loop // $refInfo = self::getRefConfig($fieldNs, $item['typeName'], $item['typeName']);
+
+		$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
+		$sql = "
+			select backRef.REMOTE_PRIMARY_KEY as PRIMARY_KEY
+					, backRef.PRIMARY_KEY as REMOTE_PRIMARY_KEY
+					, backRef.REMOTE_TYPENAME as REMOTE_TYPENAME
+					, backRef.A_STATUS as A_STATUS
+					, 0 as TRANSACTION_ID
+					, {$lastActionDateField} as A_LAST_ACTION_DATE
+			from `{$backRefTable}` backRef
+		";
+		DBG::log($sql, 'sql', "generateRefSelectSqlByBackRef");
+		return $sql;
+	}
 	public static function generateRefSelectSqlByFlatRelationCache($rootObjectNamespace, $childName) { // CRM_REF_CONFIG
 		$appInfo = DB::getPDO()->fetchValue("
 			select f.appInfo
@@ -280,7 +325,7 @@ class ACL {
 				and f.fieldNamespace = '{$childName}'
 		");
 		if (!$appInfo) throw new Exception("Missing app:info for field '{$rootObjectNamespace}/{$childName}'");
-		$appInfo = @json_decode($appInfo, $assoc = true);
+		$appInfo = ACL::decodeAppInfoJson($appInfo);
 		if (empty($appInfo)) throw new Exception("Empty app:info for field '{$rootObjectNamespace}/{$childName}'");
 
 		DBG::log(['$appInfo'=>$appInfo, '$rootObjectNamespace'=>$rootObjectNamespace, '$childName'=>$childName], 'array', "\$appInfo");
@@ -409,14 +454,18 @@ class ACL {
 		return $sql;
 	}
 	public static function setRefSource($rootObjectNamespace, $childName, $source, $viewSelectSql = null) { // CRM_REF_CONFIG
-		if (!in_array($source, ['view', 'table'])) throw new Exception("Wrong param source - expected 'table' or 'view'");
+		if (!in_array($source, ['view', 'table', 'backRef'])) throw new Exception("Wrong param source - expected 'table', 'view' or 'backRef'");
 		if ('view' === $source && !$viewSelectSql) throw new Exception("Missing create view sql");
 		$refInfo = self::getRefConfig($rootObjectNamespace, $childName);
-		if ($source != $refInfo['SOURCE']) {
+		if (true) { // if ($source != $refInfo['SOURCE']) {
 			if ('view' === $source) {
 				$refTableName = "CRM__#REF_TABLE__{$refInfo['ID']}_VIEW";
 				DB::getPDO()->execSql(" CREATE OR REPLACE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `{$refTableName}` AS {$viewSelectSql} ");
 			}
+			if ('backRef' === $source) {
+				$refTableName = "CRM__#REF_TABLE__{$refInfo['ID']}_VIEW";
+				DB::getPDO()->execSql(" CREATE OR REPLACE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `{$refTableName}` AS {$viewSelectSql} ");
+			}
 			$affected = DB::getPDO()->update("CRM_REF_CONFIG", 'ID', $refInfo['ID'], [
 				'SOURCE' => $source,
 			]);
@@ -466,6 +515,11 @@ class ACL {
 				DBG::log($e);
 			}
 		}
+		try {
+			DB::getPDO()->execSql(" ALTER TABLE `CRM_REF_CONFIG` CHANGE `SOURCE` `SOURCE` enum('table', 'view', 'backRef') not null default 'table' ");
+		} catch (Exception $e) {
+			DBG::log($e);
+		}
 		if (empty($refInfo)) {
 			$refInfo = [ 'ID' => 0, 'A_STATUS' => 'WAITING', 'VERSION' => 0, 'SOURCE' => 'table' ];
 			$refInfo['ID'] = DB::getPDO()->insert("CRM_REF_CONFIG", [
@@ -515,13 +569,21 @@ class ACL {
 			select c.CHILD_NAME as namespace
 				, c.A_STATUS
 				, c.ID
+				, c.SOURCE
 			from CRM_REF_CONFIG c
 			where c.ROOT_OBJECT_NS = :namespace
-				and c.A_STATUS = 'NORMAL'
+			--	and c.A_STATUS = 'NORMAL'
 		", [
 			':namespace' => $namespace,
 		]);
 	}
+	public static function updateChildRefSource($id, $source) {
+		if (!$id) throw new Exception("Missing id");
+		if (!in_array($source, ['view', 'table', 'backRef'])) throw new Exception("Wrong source");
+		return DB::getPDO()->update('CRM_REF_CONFIG', 'ID', $id, [
+			'SOURCE' => $source
+		]);
+	}
 
 	public static function fetchRefs($namespace, $childNamespace, $primaryKey, $params = []) { // TODO: $params: limit, total
 		if (!$namespace) throw new Exception("Missing namespace");

+ 77 - 6
SE/se-lib/Route/Storage/AclReinstall.php

@@ -39,12 +39,17 @@ class Route_Storage_AclReinstall extends RouteBase {
 				$objFieldAcl = new Schema_SystemObjectFieldStorageAcl();
 				$objFieldAcl->updateCache($namespace);
 
-				DBG::nicePrint([
-					'idInstance' => ACL::getInstanceId($namespace),
-					'rootInstance' => ACL::getRootNamespace($namespace),
-					'conf' => ACL::fetchInstanceConfig($namespace),
-					'table' => ACL::getInstanceTable($namespace),
-				], "dbg");
+				try {
+					$dbgInfo = [
+						'idInstance' => ACL::getInstanceId($namespace),
+						'rootInstance' => ACL::getRootNamespace($namespace),
+						'conf' => ACL::fetchInstanceConfig($namespace),
+						// 'table' => ACL::getInstanceTable($namespace), // Object structure not installed 'default_db/{tableName}'
+					];
+					DBG::nicePrint($dbgInfo, "dbg");
+				} catch (Exception $e) {
+					UI::alert('warning', $e->getMessage());
+				}
 
 				$item = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => '*,field' ]);
 
@@ -89,6 +94,72 @@ class Route_Storage_AclReinstall extends RouteBase {
 					// TODO: create missing refConfig - field is not in $childRefList
 				}
 
+				if ('AntAcl' === $item['_type']) { // fix ref config by appInfo
+					$refFields = array_filter($item['field'], function ($field) {
+						return ('ref:' === substr($field['xsdType'], 0, 4));
+					});
+					DBG::log($refFields, 'array', "DBG \$refFields");
+					// analyze
+					$toMakeRefEvaluateByBackRef = false;
+					$toMakeRefAsView = false;
+					foreach ($refFields as $field) {
+						$fieldName = $field['fieldNamespace'];
+						$appInfo = ACL::decodeAppInfoJson($field['appInfo']);
+						DBG::log($appInfo, 'array', "DBG field({$fieldName}) \$appInfo");
+						// $appInfo['flat_relation_cache'] = [ ... ]
+						// $appInfo['flat_relation_cache'] = [ ... ]
+						// $appInfo['flat_relation_cache']['@backref_evaluate'] = 'true'
+						// $appInfo['flat_relation_cache']['source'] = [ ... ]
+						// $appInfo['flat_relation_cache']['source']['@name'] => 'krs'
+						// $appInfo['flat_relation_cache']['source']['@xpath'] => 'default_db__x3A__BI_audit_KRS:BI_audit_KRS/krs'
+						// $appInfo['flat_relation_cache']['source']['@ref_engine'] => 'view'
+						if (!empty($appInfo['flat_relation_cache'])) {
+							$flatCache = $appInfo['flat_relation_cache'];
+							if ('true' === V::get('@backref_evaluate', '', $flatCache)) {
+								$toMakeRefEvaluateByBackRef = true;
+							}
+							if (!empty($appInfo['flat_relation_cache']['source'])) {
+								$sourceInfo = $appInfo['flat_relation_cache']['source'];
+								if ('view' === V::get('@ref_engine', '', $sourceInfo)) {
+									$toMakeRefAsView = true;
+								}
+							}
+						}
+					}
+					// update ref config
+					if ($toMakeRefAsView && !$toMakeRefEvaluateByBackRef) {
+						UI::alert('danger', "TODO: set ref config to 'view' - field({$fieldName})");
+					}
+					if ($toMakeRefEvaluateByBackRef) { // $toMakeRefAsView nie ma znaczenia - czyta wg configa dla backRef
+						try {
+							$fieldRefConfRow = array_filter($childRefList, function ($childRef) use ($fieldName) {
+								return ($fieldName === $childRef['namespace']);
+							});
+							if (!$fieldRefConfRow) throw new Exception("Nie znaleziono konfiguracji dla powiązania {$fieldName}");
+							if (count($fieldRefConfRow) > 1) throw new Exception("BUG Znaleziono za dużo konfiguracji dla powiązania {$fieldName}");
+							$fieldRefConfRow = $fieldRefConfRow[0];
+							DBG::nicePrint($fieldRefConfRow, "\$fieldRefConfRow - TODO field({$fieldName})");
+							// [namespace] => default_db__x3A__BI_audit_KRS:BI_audit_KRS
+							// [A_STATUS] => NORMAL
+							// [ID] => 118
+							// [SOURCE] => view
+							$refTable = ACL::getRefTable($item['namespace'], $fieldName); // updates ref_config_table if needed
+							try {
+								$refSelect = ACL::generateRefSelectSqlByBackRef($item['namespace'], $fieldName);
+								ACL::setRefSource($item['namespace'], $fieldName, 'backRef', $refSelect);
+							} catch (Exception $e) {
+								DBG::log($e);
+								UI::alert('danger', $e->getMessage());
+							}
+							if ('backRef' !== $fieldRefConfRow['SOURCE']) {
+								ACL::updateChildRefSource($fieldRefConfRow['ID'], 'backRef');
+							}
+						} catch (Exception $e) {
+							UI::alert('danger', $e->getMessage());
+						}
+					}
+				}
+
 				{
 					if ('AntAcl' === $item['_type']) {
 						$dbName = DB::getPDO()->getDatabaseName();

+ 22 - 13
SE/se-lib/Route/Storage/AclStruct.php

@@ -768,6 +768,11 @@ class Route_Storage_AclStruct extends RouteBase {
 							DBG::log($e);
 						}
 					}
+					$labelRefSource = [
+						'table' => "Tabela",
+						'view' => "Widok",
+						'backRef' => "BackRef",
+					];
 					return [
 						'child ref name' => $field['fieldNamespace'],
 						// 'xsdType' => $field['xsdType'], // always === "ref:{$field['fieldNamespace']}"
@@ -790,19 +795,23 @@ class Route_Storage_AclStruct extends RouteBase {
 									// 	'href' => $thisGetLink('setFieldRefConfig'),
 									// 	'data' => [ 'namespace' => $refNamespace, 'field' => $field['fieldNamespace'], 'do' => 'view' ]
 									// ]),
-									( 'table' === $refSource ) ? 'Tabela ' : 'Widok ',
-									UI::hButtonPost("Tabela ref", [
-										'title' => "Według wygenerowanej tabeli REF",
-										'class' => "btn btn-xs btn-default" . ( 'table' === $refSource ? ' disabled' : '' ),
-										// 'href' => $thisGetLink('setFieldRefConfig'),
-										'data' => [ 'namespace' => $refNamespace, 'field' => $field['fieldNamespace'], '_postTask' => 'setFieldRefConfig', 'source' => 'table' ]
-									]),
-									UI::hButtonPost("Widok (cache)", [
-										'title' => "Według flat_relation_cache",
-										'class' => "btn btn-xs btn-default" . ( 'view' === $refSource ? ' disabled' : '' ),
-										// 'href' => $thisGetLink('setFieldRefConfig'),
-										'data' => [ 'namespace' => $refNamespace, 'field' => $field['fieldNamespace'], '_postTask' => 'setFieldRefConfig', 'source' => 'view' ]
-									]),
+									$labelRefSource[$refSource] . ' ',
+									('view' === $refSource)
+									?	UI::hButtonPost("Zmień na tabelę", [
+											'title' => "Według wygenerowanej tabeli REF",
+											'class' => "btn btn-xs btn-default" . ( 'table' === $refSource ? ' disabled' : '' ),
+											// 'href' => $thisGetLink('setFieldRefConfig'),
+											'data' => [ 'namespace' => $refNamespace, 'field' => $field['fieldNamespace'], '_postTask' => 'setFieldRefConfig', 'source' => 'table' ]
+										])
+									:	'',
+									('table' === $refSource)
+									?	UI::hButtonPost("Zmień na widok", [
+											'title' => "Według flat_relation_cache",
+											'class' => "btn btn-xs btn-default" . ( 'view' === $refSource ? ' disabled' : '' ),
+											// 'href' => $thisGetLink('setFieldRefConfig'),
+											'data' => [ 'namespace' => $refNamespace, 'field' => $field['fieldNamespace'], '_postTask' => 'setFieldRefConfig', 'source' => 'view' ]
+										])
+									: '',
 								]
 						),
 						'@class' => ($isInstalled) ? "success" : "danger",

+ 7 - 2
SE/se-lib/XML.php

@@ -290,10 +290,15 @@ class XML {
 						foreach ($c[2] as $cc) {
 							if ('appinfo' == XML::getTagName($cc[0])) {
 								foreach ($cc[2] as $appTag) {
-									$appInfo[XML::getTagName($appTag[0])] = XML::readAppInfoRecurse($docArray, $appTag);
+									$appInfo[ XML::getTagName($appTag[0]) ] = XML::readAppInfoRecurse($docArray, $appTag);
 								}
 							}
 						}
+						DBG::log($appInfo, 'array', "xsd:annotation/xsd:appinfo '{$fieldName}' \$appInfo");
+						//	<xs:annotation>
+						//		<xs:appinfo>
+						//			<system_cache__appinfo:flat_relation_cache system_cache__appinfo:backref_evaluate="true">
+						//				<system_cache__appinfo:source system_cache__appinfo:name="krs" system_cache__appinfo:xpath="default_db__x3A__BI_audit_KRS:BI_audit_KRS/krs" system_cache__appinfo:ref_engine="view"/>
 					} break;
 					case 'simpleType': break; // skip xsd:element/xsd:simpleType @see findElementRestrictions
 					default: {
@@ -327,7 +332,7 @@ class XML {
 			$appInfo['@' . XML::getTagName($attrName)] = $attrVal;
 		}
 		if (!empty($nodeArray[2])) foreach ($nodeArray[2] as $appTag) {
-			$appInfo[XML::getTagName($appTag[0])] = XML::readAppInfoRecurse($docArray, $appTag);
+			$appInfo[ XML::getTagName($appTag[0]) ] = XML::readAppInfoRecurse($docArray, $appTag);
 		}
 		// TODO: text nodes
 		return $appInfo;