* @package company * @version $Id: model.php 5086 2013-07-10 02:25:22Z wyd621@gmail.com $ * @link https://www.zentao.net */ ?> loadModel('bi'); } /** * Verify the sql with select statement. * * @param string $sql * @access public * @return void */ public function verifySqlWithModify($sql) { $this->app->loadClass('sqlparser', true); $parser = new sqlparser($sql); if(count($parser->statements) == 0) return array('result' => 'fail', 'message' => $this->lang->dataview->empty); if(count($parser->statements) > 1) return array('result' => 'fail', 'message' => $this->lang->dataview->onlyOne); $statement = $parser->statements[0]; if($statement instanceof PhpMyAdmin\SqlParser\Statements\SelectStatement == false) return array('result' => 'fail', 'message' => $this->lang->dataview->allowSelect); return true; } /** * 获取模块名数组。 * Get module names. * * @param array $tables * @access public * @return array */ public function getModuleNames(array $tables): array { $moduleNames = array(); foreach($tables as $table) { /* 没有带zt_的表忽视掉。 */ if(strpos($table, $this->config->db->prefix) === false) continue; /* 过滤掉zt_后获取到模块名。 */ $module = str_replace($this->config->db->prefix, '', $table); if(!preg_match("/^[a-zA-Z]+$/", $module)) continue; /* 某些比较特殊的模块需要单独处理。 */ if($module == 'case') $module = 'testcase'; if($module == 'module') $module = 'tree'; /* Code for workflow.*/ if(strpos($module, 'flow_') !== false) { /* 如果是工作流创建的表,则将工作流的字段名当做语言项初始化。 */ $moduleName = substr($module, 5); $flowFields = $this->loadModel('workflowfield')->getFieldPairs($moduleName); $this->lang->$moduleName = new stdclass(); foreach($flowFields as $flowField => $fieldName) { if(!$flowField) continue; $this->lang->$moduleName->$flowField = $fieldName; } $moduleNames[$table] = $module; } else { /* 如果不是工作流创建的表,则直接读取这个模块的语言项。 */ if($this->app->loadLang($module)) { if($module == 'project') $this->lang->project->statusList += $this->lang->dataview->projectStatusList; $moduleNames[$table] = $module; } } } return $moduleNames; } /** * 获取别名数组。 * Get alias names. * * @param object $statement * @param array $moduleNames * @access public * @return array */ public function getAliasNames(object $statement, array $moduleNames): array { $aliasNames = array(); if(isset($statement->from)) { foreach($statement->from as $from) { if(isset($moduleNames[$from->table])) { $aliasNames[$from->alias] = $from->table; } } } if(isset($statement->join)) { foreach($statement->join as $join) { if(isset($moduleNames[$join->expr->table])) { $aliasNames[$join->expr->alias] = $join->expr->table; } } } return $aliasNames; } /** * 合并字段。 * Merge fields. * * @param array $dataFields * @param array $sqlFields * @param array $moduleNames * @param array $aliasNames * @access public * @return array */ public function mergeFields(array $dataFields, array $sqlFields, array $moduleNames, array $aliasNames = array()): array { $mergeFields = array(); $relatedObject = array(); $workflowFields = array(); foreach($dataFields as $field) { $mergeFields[$field] = $field; $relatedObject[$field] = current($moduleNames); $fieldName = $field; /* Such as $sqlFields['id'] = zt_task.id. */ if(isset($sqlFields[$field]) && strrpos($sqlFields[$field], '.') !== false) { $sqlField = $sqlFields[$field]; $table = substr($sqlField, 0, strrpos($sqlField, '.')); $fieldName = substr($sqlField, strrpos($sqlField, '.') + 1); if(isset($moduleNames[$table])) { $moduleName = $moduleNames[$table]; list($mergeFields[$field], $relatedObject[$field], $workflowFields) = $this->processMergeFields($moduleName, $field, $fieldName, $workflowFields); continue; } elseif(isset($aliasNames[$table])) { $moduleName = $moduleNames[$aliasNames[$table]]; list($mergeFields[$field], $relatedObject[$field], $workflowFields) = $this->processMergeFields($moduleName, $field, $fieldName, $workflowFields); continue; } } if(strpos(join(',', $sqlFields), '.*') !== false) { /* Such as $sqlFields['zt_task.*'] = zt_task.*. */ $existField = false; foreach($sqlFields as $sqlField) { if(strrpos($sqlField, '.*') !== false) { $table = substr($sqlField, 0, strrpos($sqlField, '.')); if(isset($moduleNames[$table])) { $moduleName = $moduleNames[$table]; list($mergeFields[$field], $relatedObject[$field], $workflowFields) = $this->processMergeFields($moduleName, $field, $fieldName, $workflowFields); $existField = true; break; } } } if($existField) continue; } foreach($moduleNames as $moduleName) { list($mergeFields[$field], $relatedObject[$field], $workflowFields) = $this->processMergeFields($moduleName, $field, $fieldName, $workflowFields); } } foreach($mergeFields as $fieldName => $fieldValue) { if(empty($fieldValue) || !is_string($fieldValue)) $mergeFields[$fieldName] = $fieldName; } foreach($mergeFields as $field => $name) $mergeFields[$field] = $this->replace4Workflow($name); return array($mergeFields, $relatedObject); } /** * Process merge fields. * * @param string $moduleName * @param string $field * @param string $fieldName * @param array $workflowFields * @access public * @return array */ public function processMergeFields($moduleName, $field, $fieldName, $workflowFields) { if(strpos($moduleName, '_flow_') !== false) { $flowPos = strpos($moduleName, '_flow_'); $moduleName = substr($moduleName, $flowPos + 6); } if(strpos($moduleName, 'flow_') !== false) $moduleName = substr($moduleName, 5); $mergeField = isset($this->lang->$moduleName->$fieldName) ? $this->lang->$moduleName->$fieldName : $field; if(!isset($workflowFields[$moduleName]) && $this->config->edition != 'open') { static $buildInModules; if(empty($buildInModules)) $buildInModules = $this->loadModel('workflow')->getBuildinModules(); $workflowFields[$moduleName] = $this->loadModel('workflowfield')->getFieldPairs($moduleName, isset($buildInModules[$moduleName]) ? '0' : 'all'); } if(isset($workflowFields[$moduleName][$fieldName])) $mergeField = $workflowFields[$moduleName][$fieldName]; return array($mergeField, $moduleName, $workflowFields); } /** * 检查查询结果的唯一性。 * Check that the column of an sql query is unique. * * @param string $sql * @param string $driverName mysql|duckdb * @param bool $repeat * @param array $columns * @access public * @return bool|array */ public function checkUniColumn(string $sql, string $driverName = 'mysql', bool $repeat = false, $columns = array()): bool|array { if(empty($columns)) $columns = $this->bi->getColumns($sql, $driverName, true); $isUnique = true; $repeatFields = array(); $fieldCounts = array_count_values(array_column($columns, 'name')); foreach($fieldCounts as $field => $value) { if($value > 1) { $isUnique = false; $repeatFields[$field] = $field; } } if($repeat) return array($isUnique, $repeatFields); return $isUnique; } /** * 获取该模块的字段列表。 * Get type options. * * @param string $objectName * @access public * @return array */ public function getTypeOptions(string $objectName): array { $schema = $this->includeTable($objectName); if(is_null($schema)) return array(); $options = array(); foreach($schema->fields as $key => $field) { //if($field['type'] == 'object') continue; $options[$key] = $field; } return $options; } /** * 替换语言项。 * Replace title for workflow. * * @param string $title * @access public * @return string */ public function replace4Workflow(string $title): string { $clientLang = $this->app->getClientLang(); $productCommonList = isset($this->config->productCommonList[$clientLang]) ? $this->config->productCommonList[$clientLang] : $this->config->productCommonList['en']; $projectCommonList = isset($this->config->projectCommonList[$clientLang]) ? $this->config->projectCommonList[$clientLang] : $this->config->projectCommonList['en']; $productCommon = $productCommonList[0]; $projectCommon = $projectCommonList[0]; if(strpos($title, strtolower($productCommon)) !== false) $title = str_replace(strtolower($productCommon), strtolower($this->lang->productCommon), $title); if(strpos($title, $productCommon) !== false) $title = str_replace($productCommon, $this->lang->productCommon, $title); return $title; } /** * 加载表配置项。 * Include table. * * @param string $table * @access public * @return object|null */ public function includeTable(string $table): object|null { $path = __DIR__ . DS . 'table' . DS . "$table.php"; if(file_exists($path)) { include $path; return $schema; } $path = $this->app->getExtensionRoot() . 'custom' . DS . 'dataview' . DS . 'table' . DS . "$table.php"; if(file_exists($path)) { include $path; return $schema; } return null; } /** * 组装父子层级结构数据。 * Gen tree options. * * @param object $tree * @param array $values * @param array $paths * @access public * @return void */ public function genTreeOptions(object &$moduleTree, array $values, array $paths) { $path = $paths[0]; if(!isset($moduleTree->children)) $moduleTree->children = array(); foreach($moduleTree->children as $child) { if($child->value == $path) { if(count($paths) > 1) return $this->genTreeOptions($child, $values, array_slice($paths, 1)); return; } } $child = new stdclass(); $child->title = $values[$path]; $child->value = $path; $moduleTree->children[] = $child; if(count($paths) > 1) return $this->genTreeOptions($child, $values, array_slice($paths, 1)); } /** * 获取默认的对象和对象的字段列表。 * Get default object and object fields. * * @access public * @return array */ public function getObjectFields() { $objectFields = array(); foreach(array_keys($this->lang->dataview->objects) as $object) $objectFields[$object] = $this->getTypeOptions($object); return $objectFields; } /** * 检查按钮是否可以点击。 * Adjust the action is clickable. * * @param object $dataview * @param string $action * @static * @access public * @return bool */ public static function isClickable(object $dataview, string $action): bool { return true; } }