<?php

// @codingStandardsIgnoreStart

 !defined('DBQUOTE') && define('DBQUOTE', '`');
$LF = LF;
$DBQUOTE = DBQUOTE;
$injectFunctionString = ""; // 方法 - 注入
$extends = '\xmvc\BaseMeta';
$extTypeInfo = "null;{$LF}";

$initValuesString = "";
$columnAttrsString = "";
$columnAttrsCommentString = "";
$autoAttrNameString = "";

$getFunctionTypeParamsString = "";
$getFunctionWhereString = "";

$virtuallyForeigns = "null;{$LF}";
$virtuallyForeignsOnUpdateAttrIndexs = "null;{$LF}";

if (!function_exists('quote')) {
    function quote(string $s)
    {
        return DBQUOTE . trim($s, DBQUOTE) . DBQUOTE;
    }
}

// 外键信息 - 自身表引用自身表 1:1
if (!function_exists('getForeignSelfFunctionString_1v1')) {
    function getForeignSelfFunctionString_1v1(object $r, array $foreign)
    {
        $attrName = $foreign['xmeta_column_attr'];
        $functionName = 'get' . ucfirst($attrName);
        $result =
'
    /** 获取 ' . $foreign['xmeta_column_caption'] . ' 的元对象*/
    public function ' . $functionName . '(): ?self
    {
        return self::get($this->' . $attrName . ');
    }
';
        return $result;
    }
}

// 外键信息 - 自身表中定义指向其他表的外键 1:1
if (!function_exists('getForeignSelfToFunctionString_1v1')) {
    function getForeignSelfToFunctionString_1v1(object $r, array $foreign)
    {
        $attrName = $foreign['xmeta_column_attr'];
        $foreignClassName = '\\app\\meta\\' . $foreign['xmeta_db_namespace'] . '\\' . $foreign['xmeta_table_classname'];
        $functionName = 'get' . $foreign['xmeta_table_classname'] . 'By' . ucfirst($attrName);
        $result =
            '
    /** 获取 ' . $foreign['xmeta_table_caption'] . ' 的元对象*/
    public function ' . $functionName . '(): ?' . $foreignClassName . '
    {
        return ' . $foreignClassName . '::get($this->' . $attrName . ');
    }
';
        return $result;
    }
}

// 外键信息 - 自身表中定义指向其他表的外键 N:1
if (!function_exists('getForeignSelfToFunctionString_Nv1')) {
    function getForeignSelfToFunctionString_Nv1(object $r, array $foreign)
    {
        $attrName = $foreign['xmeta_column_attr'];
        $foreignClassName = '\\app\\meta\\' . $foreign['xmeta_db_namespace'] . '\\' . $foreign['xmeta_table_classname'];
        $functionName = 'get' . $foreign['xmeta_table_classname'] . 'By' . ucfirst($attrName);
        $result =
            '
    /** 获取 ' . $foreign['xmeta_table_caption'] . ' 的元对象*/
    public function ' . $functionName . '(): ?' . $foreignClassName . '
    {
        return ' . $foreignClassName . '::get($this->' . $attrName . ');
    }
';
        return $result;
    }
}

// 外键信息 - 自身表中定义指向其他表的外键 M:N
if (!function_exists('getForeignSelfToFunctionString_MvN')) {
    function getForeignSelfToFunctionString_MvN(object $r, array $foreign)
    {
        $attrName = $foreign['xmeta_column_attr'];
        $foreignClassName = '\\app\\meta\\' . $foreign['xmeta_db_namespace'] . '\\' . $foreign['xmeta_table_classname'];
        $functionName = 'get' . $foreign['xmeta_table_classname'];
        $result =
            '
    /** 获取 ' . $foreign['xmeta_table_caption'] . ' 的元对象*/
    public function ' . $functionName . '(): ?' . $foreignClassName . '
    {
        return ' . $foreignClassName . '::get($this->' . $attrName . ');
    }
';
        return $result;
    }
}

// 外键信息 - 来自其他表中定义的指向自身的外键 1:1
if (!function_exists('getForeignFromAnyFunctionString_1v1')) {
    function getForeignFromAnyFunctionString_1v1(object $r, array $foreign)
    {
        $attrName = $foreign['xmeta_column_attr'];
        $attrName2 = $foreign['xmeta_column_attr#2'];
        $foreignClassName = '\\app\\meta\\' . $foreign['xmeta_db_namespace'] . '\\' . $foreign['xmeta_table_classname'];
        $functionName = 'get' . $foreign['xmeta_table_classname'] . 'By' . ucfirst($attrName2);
        $result =
            '
    /** 获取 ' . $foreign['xmeta_table_caption'] . ' 的元对象 */
    public function ' . $functionName . '(): ?' . $foreignClassName . '
    {
        return ' . $foreignClassName . '::get($this->' . $attrName2 . ');
    }
';
        return $result;
    }
}

// 外键信息 - 来自其他表中定义的指向自身的外键 N:1
if (!function_exists('getForeignFromAnyFunctionString_Nv1')) {
    function getForeignFromAnyFunctionString_Nv1(object $r, array $foreign)
    {
        $attrName = $foreign['xmeta_column_attr'];
        $attrName2 = $foreign['xmeta_column_attr#2'];
        $caption = $foreign['xmeta_column_caption'];
        $caption2 = $foreign['xmeta_column_caption#2'];

        $foreignClassName = '\\app\\meta\\' . $foreign['xmeta_db_namespace'] . '\\' . $foreign['xmeta_table_classname'];
        $functionNameFrom = 'get' . $foreign['xmeta_table_classname'] . 'ListFrom' . ucfirst($attrName) ;
        $result =
            '
    /** 获取 ' . $foreign['xmeta_table_caption'] 
    . ' 的元对象集合, 来自它的 ' . $attrName . ' (' . $caption . ') 关联 ' 
    . $foreign['xmeta_table_caption#2'] . ' 的 ' . $attrName2 . ' (' . $caption2 . ') */
    public function ' . $functionNameFrom . '(array $conditions = []): array
    {
        $arr = [\'' . $attrName . '\' => $this->' . $attrName2 .'];
        if ($conditions) {
            $arr = array_merge($arr, $conditions);
        }
        return ' . $foreignClassName . '::filter($arr);
    }
';
        return $result;
    }
}


// 外键信息 - 来自其他表中定义的指向自身的外键 M:N
if (!function_exists('getForeignFromAnyFunctionString_MvN')) {
    function getForeignFromAnyFunctionString_MvN(object $r, array $foreign)
    {
        $attrName = $foreign['xmeta_column_attr'];
        $attrName2 = $foreign['xmeta_column_attr#2'];
        $caption = $foreign['xmeta_column_caption'];
        $caption2 = $foreign['xmeta_column_caption#2'];

        $foreignClassName = '\\app\\meta\\' . $foreign['xmeta_db_namespace'] . '\\' . $foreign['xmeta_table_classname'];
        $functionName = 'get' . $foreign['xmeta_table_classname'] . 'List';
        $fullFunctionName = 'get' . $foreign['xmeta_table_classname'] . 'FullData';

        $foreignRows = $r->this->getForeignRows($foreign['xmeta_foreign_db_name'], $foreign['xmeta_foreign_table_name']);

        // 全量数据返回函数 部分内容
        $fullInfo = [];
        foreach ($foreignRows as $row) {
            if (
                $row['xmeta_foreign_type'] == 'M:N'
                && $row['xmeta_foreign_db_name'] == $foreign['xmeta_foreign_db_name']
                && $row['xmeta_foreign_table_name'] == $foreign['xmeta_foreign_table_name']
                && ($row['xmeta_foreign_db_name2'] != $foreign['xmeta_foreign_db_name2'] || $row['xmeta_foreign_table_name2'] != $foreign['xmeta_foreign_table_name2'])
            ) {
                $fullInfo['JOIN'][] = sprintf("                '%s(%s)' => ['%s' => '@%s']",
                    $row['xmeta_table_classname#2'],
                    $row['xmeta_table_classname#2'],
                    $row['xmeta_table_classname#2'] . '.' . $row['xmeta_column_attr#2'],
                    $row['xmeta_table_classname'] . '.' . $row['xmeta_column_attr']
                );
                $rowClass = '\\app\\meta\\' . $row['xmeta_db_namespace#2'] . '\\' . $row['xmeta_table_classname#2'];
                $fullInfo['return'][] = sprintf("                    '%s' => %s::fromDBRow(\$row)", $row['xmeta_table_classname#2'], $rowClass);
           }
        }
        if ($fullInfo) {
            $fullInfo['JOIN'] = implode(",\n", $fullInfo['JOIN']);
            $fullInfo['return'] = implode(",\n", $fullInfo['return']);
        }
        //echo '?' . '>';
        //dd($foreignRows);
        $result = '';
        $result .=
            "
    /**
     * 获取 {$foreign['xmeta_table_caption']} 的元对象集合
     * @param array \$conditions 查询条件, 默认使用当前 {$attrName2} ({$caption2}) 关联
     * {$foreign['xmeta_table_caption']} 的 {$attrName} ({$caption})
     */
    public function {$functionName}(array \$conditions = []): array
    {
        \$arr = ['{$attrName}' => \$this->{$attrName2}];
        if (\$conditions) {
            \$arr = array_merge(\$arr, \$conditions);
        }
        return {$foreignClassName}::filter(\$arr);
    }
";
        $result .=
            "
    /**
     * 获取 {$foreign['xmeta_table_caption']} 的全量对象集合，包含有 M:N 的关联对象
     * @param array \$conditions 查询条件
     * @return array [['关联表对象名' => 关联表对象], ['关联目标对象名' => 关联目标对象]]
     */
    public function {$fullFunctionName}(array \$conditions = []): array
    {
        \$arr = [
            'JOIN' => [
{$fullInfo['JOIN']}
            ],
            'WHERE' => [
                '{$foreign['xmeta_table_classname']}.{$attrName}' => \$this->{$attrName2}
            ]
        ];
        if (\$conditions) {
            if (isset(\$conditions['WHERE'])) {
                \$arr['WHERE'] = array_merge(\$arr['WHERE'], \$conditions['WHERE']); // 先单独对 WHERE 键做合并操作
                \$arr = array_merge(\$arr, \$conditions);
            } else {
                \$arr['WHERE'] = array_merge(\$arr['WHERE'], \$conditions); // 省略 WHERE 键的方式
            }
        }
        if (!class_exists('{$foreignClassName}', false)) {
            {$foreignClassName}::staticInit();
        }
        \$arr = self::conditionsToWhere(\$arr, ['{$foreign['xmeta_table_classname']}' => ['class' => '{$foreignClassName}']]);
        \$q = self::getDB()->newSqler();
        \$rows = \$q->table('`{$foreign['xmeta_db_name']}`.`{$foreign['xmeta_table_name']}`', '{$foreign['xmeta_table_classname']}')->where(\$arr)->all();
        \$data = [];
        if (\$rows) {
            foreach (\$rows as \$row) {
                \$data[] = [
                    '{$foreign['xmeta_table_classname']}' => {$foreignClassName}::fromDBRow(\$row),
 {$fullInfo['return']}
                ];
            }
        }
        return \$data;
    }
";

        foreach ($foreignRows as $row) {
            if (
                $row['xmeta_foreign_type'] == 'M:N'
                && $row['xmeta_foreign_db_name'] == $foreign['xmeta_foreign_db_name']
                && $row['xmeta_foreign_table_name'] == $foreign['xmeta_foreign_table_name']
                && ($row['xmeta_foreign_db_name2'] != $foreign['xmeta_foreign_db_name2'] || $row['xmeta_foreign_table_name2'] != $foreign['xmeta_foreign_table_name2'])
            ) {
                $rowAttrName = $row['xmeta_column_attr'];
                $rowAttrName2 = $row['xmeta_column_attr#2'];
                $rowCaption = $row['xmeta_column_caption'];
                $rowCaption2 = $row['xmeta_column_caption#2'];
                $rowForeignClassName = '\\app\\meta\\' . $row['xmeta_db_namespace#2'] . '\\' . $row['xmeta_table_classname#2'];
                $rowFunctionName = 'get' . $row['xmeta_table_classname#2'] . 'List';

                $lnkAs = $foreign['xmeta_table_classname'];
                $result .= 
"
    /**
     * 获取 {$row['xmeta_table_caption#2']} 的元对象集合
     * @param array \$conditions 查询条件
     * 1 使用当前 {$attrName2} ({$caption2}) 关联
     * 2 关联 {$foreign['xmeta_table_caption']} (查询别名 {$lnkAs}) 的 {$attrName} ('{$caption}) 获取 {$rowAttrName} ({$rowCaption})
     * 3 取 {$row['xmeta_table_caption#2']} 的集合 (查询别名 {$row['xmeta_table_classname#2']} 当前此别名可省略)
     * @return {$rowForeignClassName} array
     */
    public function {$rowFunctionName}(array \$conditions = []): array
    {
        \$arr = [
            'FIELDS' => [
                '{$row['xmeta_table_classname#2']}.*'
            ],
            'JOIN' => [
                '{$foreign['xmeta_table_classname']}({$lnkAs})' => ['{$lnkAs}.{$rowAttrName}' => '@{$rowAttrName2}']
            ],
            'WHERE' => [
                '{$lnkAs}.{$attrName}' => \$this->{$attrName2}
            ]
        ];
        if (\$conditions) {
            if (isset(\$conditions['WHERE'])) {
                \$arr['WHERE'] = array_merge(\$arr['WHERE'], \$conditions['WHERE']); // 先单独对 WHERE 键做合并操作
                \$arr = array_merge(\$arr, \$conditions);
            } else {
                \$arr['WHERE'] = array_merge(\$arr['WHERE'], \$conditions); // 省略 WHERE 键的方式
            }
        }
        return {$rowForeignClassName}::filter(\$arr);
    }
";
            }
        }
        return $result;
    }
}

$fieldAttrLinks = [];
$attrFieldLinks = [];
foreach ($columnRows as $row) {
    $columnName = $row['xmeta_column_name'];
    $attrName = $row['xmeta_column_attr'];
    $fieldAttrLinks[$columnName] = $attrName;
    $attrFieldLinks[$attrName] = $columnName;
}

if ($r->type == 'tree') {
    $extends = '\xmvc\BaseMetaTree';
    $treeType = $r->typeInfo['type'] ?? 'tree'; // tree|lrtree
    $rootParentValue = $r->typeInfo['rootParentValue'] ?? 'null';
    // 字段
    $fId = $r->primarys[0]; // 树 ID 只支持一个主键
    $mustTreeKeys = ['parentId','level','isEnd','sort','fullPath'];
    foreach ($mustTreeKeys as $mustTreeKey) {
        if (!isset($r->typeInfo[$mustTreeKey])) {
            throw new Exception($r->tableName . ' 表缺少树字段 ' . $mustTreeKey. ' 的定义, 完整的树字段需要包括 ' . implode(',', $mustTreeKeys));
        }
    }
    $fParentId = $this->parseMyPrefix($r->typeInfo['parentId'], $r->prefix);
    $fLevel = $this->parseMyPrefix($r->typeInfo['level'], $r->prefix);
    $fIsEnd = $this->parseMyPrefix($r->typeInfo['isEnd'], $r->prefix);
    $fSort = $this->parseMyPrefix($r->typeInfo['sort'], $r->prefix);
    $fFullPath = $this->parseMyPrefix($r->typeInfo['fullPath'], $r->prefix);
    $fLeft = $this->parseMyPrefix($r->typeInfo['left'] ?? '', $r->prefix);
    $fRight = $this->parseMyPrefix($r->typeInfo['right'] ?? '', $r->prefix);
    //属性
    $aId = $fieldAttrLinks[$fId];
    $aParentId = $fieldAttrLinks[$fParentId];
    $aLevel = $fieldAttrLinks[$fLevel];
    $aIsEnd = $fieldAttrLinks[$fIsEnd];
    $aSort = $fieldAttrLinks[$fSort];
    $aFullPath = $fieldAttrLinks[$fFullPath];
    $aLeft = $fLeft ? $fieldAttrLinks[$fLeft] : '';
    $aRight = $fRight ? $fieldAttrLinks[$fRight] : '';

    // 构造内容
    $extTypeInfo = "
    /** 根结点的父值 */
    protected static mixed \$rootNodeParentValue;
    /** ID 字段名 field */
    protected static string \$fId;
    /** 父ID 字段名 */
    protected static string \$fParentId;
    /** 层级 字段名 */
    protected static string \$fLevel;
    /** 终节点 字段名 */
    protected static string \$fIsEnd;
    /** 序号 字段名 */
    protected static string \$fSort;
    /** 路径 字段名, 存储的时末尾要加逗号 (索引考虑) */
    protected static string \$fFullPath;
    /** 左值 字段名 */
    protected static string \$fLeft;
    /** 右值 字段名 */
    protected static string \$fRight;

    /** ID 属性名 attr */
    protected static string \$aId;
    /** 父ID 属性名 */
    protected static string \$aParentId;
    /** 层级 属性名 */
    protected static string \$aLevel;
    /** 终节点 属性名 */
    protected static string \$aIsEnd;
    /** 序号 属性名 */
    protected static string \$aSort;
    /** 路径 属性名, 存储的时末尾要加逗号 (索引考虑) */
    protected static string \$aFullPath;
    /** 左值 属性名 */
    protected static string \$aLeft;
    /** 右值 属性名 */
    protected static string \$aRight;

";
    // 静态初始化内容
    $extTypeInfo =
    "
        // 树的根结点的父值
        self::\$rootNodeParentValue = {$rootParentValue};
        // 树的字段名称
        self::\$fId = '{$fId}';
        self::\$fParentId = '{$fParentId}';
        self::\$fLevel = '{$fLevel}';
        self::\$fIsEnd = '{$fIsEnd}';
        self::\$fSort = '{$fSort}';
        self::\$fFullPath = '{$fFullPath}';
        self::\$fLeft = '{$fLeft}';
        self::\$fRight = '{$fRight}';

        // 树的属性名称
        self::\$aId = '{$aId}';
        self::\$aParentId = '{$aParentId}';
        self::\$aLevel = '{$aLevel}';
        self::\$aIsEnd = '{$aIsEnd}';
        self::\$aSort = '{$aSort}';
        self::\$aFullPath = '{$aFullPath}';
        self::\$aLeft = '{$aLeft}';
        self::\$aRight = '{$aRight}';
";
    $injectFunctionString = 
    "// 树的当前记录值
        \$meta->vId = \$meta->{$aId};
        \$meta->vParentId = \$meta->{$aParentId};
        \$meta->vLevel = \$meta->{$aLevel};
        \$meta->vIsEnd = \$meta->{$aIsEnd};
        \$meta->vSort = \$meta->{$aSort};
        \$meta->vFullPath = \$meta->{$aFullPath};";
    if ($aLeft && $aRight) {
        $injectFunctionString .=
"        \$meta->vLeft = \$meta->{$aLeft};
        \$meta->vRight = \$meta->{$aRight};";
    }      
} // tree


$columns = new stdClass;
foreach ($columnRows as $row) {
    $columnName = $row['xmeta_column_name'];
    $attrName = $row['xmeta_column_attr'];
    $caption = $row['xmeta_column_caption'];
    $mysqlType = $row['xmeta_column_type'];
    $mysqlTypeParam = $row['xmeta_column_type_param'];
    $mysqlDefaultValue = $row['xmeta_column_default_value'];
    $attrType = $this->mysqlTypeToPhpType($mysqlType, $mysqlTypeParam);
    $lengthArr = $this->mysqlTypeLengths($mysqlType, $mysqlTypeParam);
    $defaultValue = $this->mysqlDefaultToPhpDefault($attrType, $mysqlDefaultValue);
    $isAuto = $row['xmeta_column_is_auto_increment'];
    $isNotNull = $row['xmeta_column_is_notnull'];
    $typeNull = (!$isNotNull || $defaultValue == 'CURRENT_TIMESTAMP' || $isAuto) ? '?' : '';
    $defaultValue = $defaultValue == 'CURRENT_TIMESTAMP' ? 'null' : $defaultValue;

    $enumInfo = $row['xmeta_column_enum_info'] ? json_decode($row['xmeta_column_enum_info'], true) : null;
    if (!$enumInfo && $mysqlType == 'ENUM') {
        $tmpArr = json_decode($mysqlTypeParam, true);
        $enumInfo = [];
        foreach ($enumInfo as $key => $value) {
            $enumInfo[$value] = $value;
        }
    }    
    $enumInfoString = "null";
    if ($enumInfo) {
        $enumInfoString = "[" . LF;
        foreach ($enumInfo as $key => $value) {
            $enumInfoString .= sprintf('                    "%s" => "%s",' . LF, $key, $value);
        }
        $enumInfoString .= "                ]";        
    }
    $columns->{$columnName} = (object)[
        'attrName' =>  $attrName,
        'columnName' =>  $columnName,
        'caption' =>  $caption,
        'mysqlType' =>  $mysqlType,
        'mysqlTypeParam' =>  $mysqlTypeParam,
        'mysqlDefaultValue' =>  $mysqlDefaultValue,
        'isAuto' =>  $isAuto,
        'comment' =>  $row['xmeta_column_comment'],
        'attrType' =>  $attrType,
        'defaultValue' =>  $defaultValue,
        'enumInfo' =>  $enumInfo,
        'enumInfoString' =>  $enumInfoString,
        'typeNull' =>  $typeNull,
        'isNotNull' =>  $isNotNull,
        'length' =>  $lengthArr[0],
        'scale' =>  $lengthArr[1],
    ];
}
foreach ($columns as $column) :
    $columnAttrsCommentString .= "* @property {$column->typeNull}{$column->attrType} \${$column->attrName} {$column->comment}{$LF}";
    $columnAttrsString .= "    protected {$column->typeNull}{$column->attrType} \${$column->attrName};{$LF}";
    if (!is_null($column->mysqlDefaultValue) || !$column->isNotNull) {
        $initValuesString .= "        \$this->{$column->attrName} = {$column->defaultValue};{$LF}";
    } elseif ($column->isAuto) {
        $initValuesString .= "        \$this->{$column->attrName} = null;{$LF}";
        $autoAttrNameString  = $column->attrName;
    }
endforeach;

if ($r->primary) {
    $attrName = $columns->{$r->primaryFieldName}->attrName;
    $attrType = $columns->{$r->primaryFieldName}->attrType;
    $typeNull = $columns->{$r->primaryFieldName}->typeNull;
    if ($getFunctionTypeParamsString != '') {
        $getFunctionTypeParamsString .= ', ';
    }
    if ($getFunctionWhereString != '') {
        $getFunctionWhereString .= ', ';
    }
    $getFunctionTypeParamsString .= "{$typeNull}{$attrType} \${$attrName}";
    $getFunctionWhereString .= "'{$DBQUOTE}{$r->primaryFieldName}{$DBQUOTE}' => \${$attrName}";
}
$getFunctionString =
'
    /**
     * 通过主键获取 ' . $r->classCaption . ' 的元对象
     * @return self
     */
    public static function get(' . $getFunctionTypeParamsString . '): ?self
    {
        return parent::baseGet([' . $getFunctionWhereString . ']);
    }
';

if ($injectFunctionString) {
    $injectFunctionString =
'

    /** 注入 */
    public static function inject($meta)
    {
        ' . $injectFunctionString .'
    }
';    
}


// 外键信息 - 自身表引用自身表
$foreignFunctionList = [];
if ($foreignSelfRows) {
    if ($r->tableName == 'xmeta_user') {
        //dd($foreignSelfRows);
    }
    foreach ($foreignSelfRows as $foreign) {
        $foreignType = $foreign['xmeta_foreign_type'];
        $foreignCount = $foreign['xmeta_foreign_count'];
        if ($foreignCount == 1) {
            switch ($foreignType) {
                case '1:1':
                    $foreignFunctionList[] = getForeignSelfFunctionString_1v1($r, $foreign);
                    break;
                case 'N:1':
                    $foreignFunctionList[] = getForeignSelfFunctionString_1v1($r, $foreign);
                    break;
                default:
                    error($r->dbName . '.' . $r->tableName . ' 外键信息 - 自身表引用自身表的类型 ' . $foreignType . ' 不被支持');
            }
        }
    }
} //end $foreignRows

// 外键信息 - 自身表中定义指向其他表的外键
if ($foreignToRows) {
    if ($r->tableName == 'xmeta_user_cfg') {
        //dd($foreignToRows);
    }
    foreach ($foreignToRows as $foreign) {
        $foreignType = $foreign['xmeta_foreign_type'];
        $foreignCount = $foreign['xmeta_foreign_count'];
        if ($foreignCount == 1) {
            switch ($foreignType) {
                case '1:1':
                    $foreignFunctionList[] = getForeignSelfToFunctionString_1v1($r, $foreign);
                    break;
                case 'N:1':
                    $foreignFunctionList[] = getForeignSelfToFunctionString_Nv1($r, $foreign);
                    break;
                case '1:N':
                    break;
                case 'M:N':
                    $foreignFunctionList[] = getForeignSelfToFunctionString_MvN($r, $foreign);
                    break;
            }
        }
    }
} //end $foreignRows

// 外键信息 - 来自其他表中定义的指向自身的外键
if ($foreignFromRows) {
    if ($r->tableName == 'xmeta_user') {
        //dd($foreignFromRows);
    }
    $virtuallyForeigns = [];
    $virtuallyForeignsOnUpdateAttrIndexs = [];
    $vfRegI = 0;
    foreach ($foreignFromRows as $foreign) {
        $foreignType = $foreign['xmeta_foreign_type'];
        $foreignCount = $foreign['xmeta_foreign_count'];
        if ($foreignCount == 1) {
            switch ($foreignType) {
                case '1:1':
                    $foreignFunctionList[] = getForeignFromAnyFunctionString_1v1($r, $foreign);
                    break;
                case 'N:1':
                    $foreignFunctionList[] = getForeignFromAnyFunctionString_Nv1($r, $foreign);                
                    break;
                case '1:N':
                    break;
                case 'M:N':
                    $foreignFunctionList[] = getForeignFromAnyFunctionString_MvN($r, $foreign);                
                    break;
            }
            // 虚拟外键约束, 非数据库约束, 用程序来实现约束
            if ($foreign['xmeta_foreign_notindb']) {
                if ($foreign['xmeta_foreign_on_update'] != 'NO ACTION') {
                    $virtuallyForeignsOnUpdateAttrIndexs[$foreign['xmeta_column_attr#2']][] = $vfRegI;
                }
                if ($foreign['xmeta_foreign_on_delete'] != 'NO ACTION' || $foreign['xmeta_foreign_on_update'] != 'NO ACTION') {
                    $virtuallyForeigns[$vfRegI] = 
            "
        [
            'binder' => [
                'class' => '\\app\\meta\\{$foreign['xmeta_db_namespace']}\\{$foreign['xmeta_table_classname']}',
                'attrName' => '{$foreign['xmeta_column_attr']}',
            ],
            'refer' => [
                'attrName' => '{$foreign['xmeta_column_attr#2']}',
            ],
            'onDelete' => '{$foreign['xmeta_foreign_on_delete']}',
            'onUpdate' => '{$foreign['xmeta_foreign_on_update']}',
        ]
";
                    $vfRegI++;
                } // 只要有一个不为 NO ACTION
            } // xmeta_foreign_notindb
        } // $foreignCount == 1
    }
    if ($virtuallyForeigns) {
        $virtuallyForeigns = "[" . implode(',', $virtuallyForeigns) . "    ];{$LF}";
    } else {
        $virtuallyForeigns = 'null;';
    }
    if ($virtuallyForeignsOnUpdateAttrIndexs) {
        $s = "[{$LF}";
        foreach ($virtuallyForeignsOnUpdateAttrIndexs as $attrName => $indexs) {
            $s .= "        '{$attrName}' => [";
            foreach ($indexs as $i => $idx) {
                if ($i > 0) {
                    $s .= ', ';
                }
                $s .= $idx;
            }
            $s .= "],{$LF}";
        }
        $s .= "    ];{$LF}";
        $virtuallyForeignsOnUpdateAttrIndexs = $s;
    }
} //end $foreignRows


// 多语言信息
foreach ($langMapRows as $row) {
    # code...
}
//dd($langMapRows);
?>
namespace app\meta\<?= $r->namespace ?>;

use Closure;
use Exception;
use xmvc\DB;
use xmvc\Sqler;
use xmvc\EventManager;

/**
* <?= $r->classCaption ?>

* - <?= $r->classComment ?>

<?= $columnAttrsCommentString ?>
*/
class <?= $r->classname ?> extends <?= $extends ?>

{
    /** 元对象 完整类名  */
    public const CLASSFULL = 'app\meta\\<?= addslashes($r->namespace) ?>\\<?= $r->classname ?>';
    /** 元对象 命名空间 不含 app\meta\ 前缀  */
    public const NAMESPACE = '<?= addslashes($r->namespace) ?>';
    /** 元对象 类名 不含命名空间 */
    public const CLASSNAME = '<?= $r->classname ?>';
    /** 数据库配置键 来自 config/database.php */
    public const DBKEY = '<?= $r->dbKey ?>';
    /** 对应的数据库名 */
    public const DBNAME = '<?= $r->dbName ?>';
    /** 对应的表名 */
    public const TABLENAME = '<?= $r->tableName ?>';
    /** 完整的数据库+表名 */
    public const TABLE = '<?= quote($r->dbName) ?>.<?= quote($r->tableName) ?>';
    /** 主键字段名 */
    public const PRIMARY_FIELD_NAME = '<?= $r->primaryFieldName?>';
    /** 主键属性名 */
    public const PRIMARY = '<?= $r->primary?>';
    /** 自增属性名称 */
    public const AUTO_ATTR = '<?= $autoAttrNameString?>';
    /** 类属性与表字段映射 */
    public const ATTR_LNK_FIELD = [
<?php foreach ($columns as $column) :?>
        '<?= $column->attrName ?>' => '<?= $column->columnName ?>',
<?php endforeach;?>
    ];
    /** 表字段与类属性映射 */
    public const FIELD_LNK_ATTR = [
<?php foreach ($columns as $column) :?>
        '<?= $column->columnName ?>' => '<?= $column->attrName ?>',
<?php endforeach;?>
    ];
    /** 属性完整定义信息 */
    public const ATTRS = [
<?php foreach ($columns as $column) :?>
        '<?= $column->attrName ?>' => [
            'caption' => '<?= addslashes($column->caption) ?>',
            'mysqlType' => '<?= $column->mysqlType ?>',
            'mysqlTypeParam' => <?= $column->mysqlTypeParam ?: 'null' ?>,
            'mysqlDefaultValue' => "<?= $column->mysqlDefaultValue ?>",
            'defaultValue' => <?= $column->defaultValue ?>,
            'isNotNull' => <?= $column->isNotNull ? 'true' : 'false' ?>,
            'phpType' => '<?= $column->attrType ?>',
            'typeNull' => '<?= $column->typeNull ?>',
            'enumInfo' => <?= $column->enumInfoString ?>,
            'length' => <?= $column->length ?? 0 ?>,
            'scale' => <?= $column->scale ?? 0 ?>,
        ],
<?php endforeach;?>
    ];
    /** 虚拟外键约束, 非数据库约束, 用程序来实现约束 */
    public const VIRTUALLY_FOREIGNS = <?= $virtuallyForeigns?>
    /** 虚拟外键约束, 更新时需要处理的属性索引 */
    public const VIRTUALLY_FOREIGNS_ONUPDATE_ATTR_INDEXS = <?= $virtuallyForeignsOnUpdateAttrIndexs?>
    /** 扩展类型定义 */
    public const EXT_TYPE_INFO = <?= $extTypeInfo?>

    /** 数据库类 */
    public static DB $metaDb;

<?= $columnAttrsString ?>
    /**
    * 构造
     * @param array $attrs 属性值数组
     * @param bool $isNewCreate 是否是新创建的记录, 如果 $attrs 空, 则 $isNewCreate 默认 true
    */
    public function __construct(array $attrs = [], bool $isNewCreate = false)
    {
<?= $initValuesString ?>
<?php if($r->type != 'table' && $typeInfoString): ?>
<?= $typeInfoString ?>
<?php endif;?>
        parent::__construct($attrs, $isNewCreate);
    }
<?= $getFunctionString ?>
<?= $injectFunctionString ?>
<?php echo implode('', $foreignFunctionList); ?>
}
