EZDML脚本快速上手

——HUZ 20191027

 

脚本能够访问模型中的所有对象及其属性,能够批量进行各种增删改查,能生成你想要的各种格式。

注意:模型目录树上有批量添加删除字段功能,比写脚本简单多了,如果仅仅是想批量增减字段,可能没有必要用脚本。

EZDML同时支持JavascriptPascal脚本(以下简称JSPAS),你可以根据需要选择一种。

一、 JAVASCRIPT脚本

本文档主要说如何用JS实现一些目的,不讲解javascript的基础知识。

打开示例文件,选中会员表,右键弹出菜单,选择“执行脚本”:

弹出脚本编辑窗口,默认就是JAVASCRIPT的示例了:

简单说下这个脚本窗口:执行NEW命令新建时会自动初始化为示例脚本,默认是JS脚本(再点一下NEW命令就切换为PAS)。JS脚本目前不支持断点和单步调试,按F9运行,F1显示帮助。脚本窗口关闭时会自动保存当前内容到临时文件(每次F9运行脚本时也会)。

在这个脚本窗口执行”Help”菜单或按F1可显示一些简单的帮助:

回到JAVASCRIPT的示例,往下翻,可以看到一共有五个示例:

1.      演示如何编写JavaScript脚本(再次新建可切换为PascalScript)遍历所有模型、表和字段

2.      演示如何编写脚本页面模板

3.      演示如何编写脚本处理文本文件

4.      演示如何编写脚本遍历对象属性

5.      演示如何在JS脚本中混合调用Pascal脚本

 

其中只有第一个没有被注释掉,我们就拿它来说事。它显然是i j k三个for循环遍历了模型、表和字段。代码很简单,不过我还是再解释一下:

 

//开始

for(var i=0; i<allModels.count; i++) //遍历所有模型,一个文件里可能有多个模型图

//if(allModels.getItem(i) == allModels.curDataModel) //判断是否为当前模型

{

  var md=allModels.getItem(i); //获得第i个模型

  curOut.add('Model'+i+': '+md.name); //输出模型名称

 

  for(var j=0; j<md.tables.count; j++)  //遍历模型中的表

  //if(md.tables.getItem(j).isSelected)   //判断表是否选中

  //if(curTable && md.tables.getItem(j).name == curTable.name) //判断表名是否与当前表名相同

  {

    var tb = md.tables.getItem(j); //获取表

    curOut.add('  Table'+j+': '+tb.name); //输出表名

 

    for(var k=0; k<tb.metaFields.count; k++) //遍历表中的字段

    {

      var fd = tb.metaFields.getItem(k);  //获取字段

      curOut.add('    Field'+k+': '+fd.name); //输出字段名

    }

  }

}

//结束

 

我们把其它示例2345的代码删除,只留下示例1,直接按F9运行,可以看到运行结果输出了所有表和字段:

 

把第12//if(md.tables.getItem(j).isSelected)这行前面的//注释符删除,再运行,这时就只输出会员表的信息了,因为我们只选中了会员表:

 

把第12行注掉,增加字段名的判断,并修改输出代码,最终改成下面的样子:

 
for(var i=0; i<allModels.count; i++)
//if(allModels.getItem(i) == allModels.curDataModel)
{
  var md=allModels.getItem(i);
 
  for(var j=0; j<md.tables.count; j++)
  //if(md.tables.getItem(j).isSelected)
  //if(curTable && md.tables.getItem(j).name == curTable.name)
  {
    var tb = md.tables.getItem(j);
 
    for(var k=0; k<tb.metaFields.count; k++)
    {
      var fd = tb.metaFields.getItem(k);
      if(fd.name.toLowerCase().indexOf("product")>=0) //判断字段名是否包含product
      {
        curOut.add('Model_'+i+': '+md.name+'  Table'+j+': '+tb.name+'    Field'+k+': '+fd.name);
      }
    }
  }
}
 

运行结果如下,只输出了符合条件的字段:

很多时候我们只要处理当前表curTable,假设我要为当前表的字段生成某个赋值代码,这时可以这样遍历字段(请注意要选一个表,以保证当前表curTable有值):

 

for(var i=0; i<curTable.metaFields.count-1; i++)

{

  var fd = curTable.metaFields.getItem(i);

  curOut.add('  if(member1.'+fd.name+'==null)');

  curOut.add('    member1.'+fd.name+'=member2.'+fd.name+';');

}

运行效果:

查询遍历基本上就这样了,下面我们要动手修改了。

我要给每一个包含字段id的表,增加一个memo备注字段(如果已存在则跳过),类型为字符串,长度512,普通索引(只是为了演示哈,其实模型目录树上有批量添加字段功能,比写脚本简单多了),代码如下:

 
for(var i=0; i<allModels.count; i++)
{
  var md=allModels.getItem(i);
  
  for(var j=0; j<md.tables.count; j++)
  {
    var tb = md.tables.getItem(j);
    
    var fd=tb.metaFields.fieldByName("id"); //查找ID字段
    if(!fd) //未找到ID字段,跳过
    {
      curOut.add('  Table'+j+': '+tb.name +' no ID found, skipped');
    }
    else //找到ID字段了
    {
      fd=tb.metaFields.fieldByName("memo"); //查找memo字段
      if(!fd) //不存在就添加
      {
        fd=tb.metaFields.newMetaField();
        fd.name='memo';
        fd.displayName='备注';
        fd.memo='这是一个演示添加的字段';
        fd.dataType='cfdtString';
        fd.dataLength=512;
        fd.indexType='cfitNormal';
        fd.nullable=true;
        curOut.add('  Table'+j+': '+tb.name +' memo field added');
      }
      else  //已存在则提示
        curOut.add('  Table'+j+': '+tb.name +' memo field exists');
    }
  }
}
 

正常来说,我们改了一个表,需要调_syncTableProps(tb)同步属性到模型中其它所有同名的表。不过,这里我们直接遍历了所有模型中的表全改了一遍,就不需要同步了。

运行结果:

回到模型图刷新一下,大部分表都加上了memo字段:

接下来我觉得memo这名字不好,我要把这些memo字段改名为remark,逻辑名为“评论”,长度改为255,代码如下:

 

for(var i=0; i<allModels.count; i++)
{
  var md=allModels.getItem(i);
 
  for(var j=0; j<md.tables.count; j++)
  {
    var tb = md.tables.getItem(j);
 
    var fd=tb.metaFields.fieldByName("memo"); //查找memo字段
    if(!fd) //未找到memo字段,跳过
    {
      curOut.add('  Table'+j+': '+tb.name +' no memo field found, skipped');
    }
    else //找到memo字段了
    {
      fd.name='remark';
      fd.displayName='评论';
      fd.dataLength=255;
      curOut.add('  Table'+j+': '+tb.name +' memo field modified');
    }
  }
}
 

运行结果:

回到模型图,刷新:

 

接下来我又后悔了,我觉得remark这字段没什么用,决定删除掉,代码如下:

 
for(var i=0; i<allModels.count; i++)
{
  var md=allModels.getItem(i);
 
  for(var j=0; j<md.tables.count; j++)
  {
    var tb = md.tables.getItem(j);
 
    var fd=tb.metaFields.fieldByName("remark"); //查找remark字段
    if(!fd) //未找到remark字段,跳过
    {
      curOut.add('  Table'+j+': '+tb.name +' no remark field found, skipped');
    }
    else //找到remark字段了,删除之
    {
      tb.metaFields.remove(fd);
      curOut.add('  Table'+j+': '+tb.name +' remark field removed');
    }
  }
}

 

运行结果:

 

好了,用JS增删改查了一遍,想必你已经有个大概了解了。

 

 

二、 PASCAL脚本

JS的人没必要看这一节。

写了JS的内容后,感觉PASCAL似乎没有必要写了,不过空着也不好,还是写点凑数吧。

还是打开示例文件,选中会员表,右键弹出菜单,选择“执行脚本”:

弹出脚本编辑窗口,默认就是JAVASCRIPT的示例:

再执行一次File/New菜单命令,切换为PASCAL脚本(如果还是JS,就再点一次):

PASCAL脚本支持断点和单步调试,按F9运行,F1显示帮助。脚本窗口关闭时会自动保存当前内容到临时文件(每次F9运行脚本时也会)。

照例解释一下:

 

var //变量声明,PASCAL比较烦人的一点就是变量声明要在最前面

  I, J, K: Integer;

  md: TCtDataModelGraph;

  tb: TCtMetaTable;

  fd: TCtMetaField;

begin  //开始

  for I:=0 to AllModels.Count-1 do  //遍历所有模型

  //if AllModels.Items[I] = AllModels.CurDataModel then  //判断如果是当前模型

  begin

    md := AllModels.Items[I];     //获取模型

    CurOut.Add('Model'+IntToStr(I)+': '+md.Name); //输出模型名称

   

    for J:=0 to md.Tables.Count-1 do    //遍历该模型中的所有表

    //if md.Tables.Items[J].IsSelected then //如果表被选中

    //if CurTable <> nil then if md.Tables.Items[J].Name = CurTable.Name then //或者表名与当前表一样

    begin

      tb := md.Tables.Items[J]; //获取表

      CurOut.Add('  Table'+IntToStr(J)+': '+tb.Name); //输出表名

   

      for K:=0 to tb.MetaFields.Count -1 do //遍历该表的所有字段

      begin

        fd := tb.MetaFields.Items[K];  //获取字段

        CurOut.Add('    Field'+IntToStr(K)+': '+fd.Name); //输出字段名

      end;

    end;

  end;

end. //PASCAL的最后结束是end加一个点

 

 

直接按F9运行,效果跟之前JS的示例是一样的:

增加字段名的判断,并修改输出代码,最终改成下面的样子:

 

var

  I, J, K: Integer;

  md: TCtDataModelGraph;

  tb: TCtMetaTable;

  fd: TCtMetaField;

begin

  for I:=0 to AllModels.Count-1 do

  //if AllModels.Items[I] = AllModels.CurDataModel then

  begin

    md := AllModels.Items[I];

   

    for J:=0 to md.Tables.Count-1 do

    //if md.Tables.Items[J].IsSelected then

    //if CurTable <> nil then if md.Tables.Items[J].Name = CurTable.Name then

    begin

      tb := md.Tables.Items[J];

   

      for K:=0 to tb.MetaFields.Count -1 do

      begin

        fd := tb.MetaFields.Items[K];

        if Pos('product', LowerCase(fd.Name))>0 then//判断字段名是否包含product

        CurOut.Add('Model_'+IntToStr(i)+': '+md.name+'  Table'+IntToStr(j)+': '+tb.name+'    Field'+IntToStr(k)+': '+fd.name);

      end;

    end;

  end;

end.

 

 

运行结果如下,只输出了符合条件的字段:

很多时候我们只要处理当前表CurTable,假设我要为当前表的字段生成某个赋值代码,这时可以这样遍历字段(请注意要选一个表,以保证当前表CurTable有值):

 

var

  I: Integer;

  fd: TCtMetaField;

begin

  for I:=0 to CurTable.MetaFields.Count -1 do

  begin

    fd := CurTable.MetaFields.Items[I];

    CurOut.Add('  if(member1.'+fd.Name+'==null)');

    CurOut.Add('    member1.'+fd.Name+'=member2.'+fd.Name+';');

  end;

end.

 

运行效果:

 

再简单说下批量加字段,要给每一个包含字段id的表,增加一个memo备注字段(如果已存在则跳过),类型为字符串,长度512,普通索引(只是为了演示,其实模型目录树上有批量添加字段功能,比写脚本简单),代码如下:

 

var

  I, J: Integer;

  md: TCtDataModelGraph;

  tb: TCtMetaTable;

  fd: TCtMetaField;

begin

  for I:=0 to AllModels.Count-1 do

  begin

    md := AllModels.Items[I];

   

    for J:=0 to md.Tables.Count-1 do

    begin

      tb := md.Tables.Items[J];

      fd := tb.metaFields.fieldByName('id');

      if fd = nil then //未找到ID字段,跳过

      begin

        curOut.add('  Table'+IntToStr(j)+': '+tb.name +' no ID found, skipped');

      end

      else

      begin

        fd := tb.metaFields.fieldByName('memo'); //查找memo字段

        if fd = nil then //不存在就添加

        begin

          fd:=tb.metaFields.newMetaField();

          fd.name:='memo';

          fd.displayName:='备注';

          fd.memo:='这是一个演示添加的字段';

          fd.dataType:=cfdtString;

          fd.dataLength:=512;

          fd.indexType:=cfitNormal;

          fd.nullable:=true;

          curOut.add('  Table'+IntToStr(j)+': '+tb.name +' memo field added');

        end

        else  //已存在则提示

          curOut.add('  Table'+IntToStr(j)+': '+tb.name +' memo field exists');

      end;

    end;

  end;

end.

 

正常来说,我们改了一个表,需要调SyncTableProps(tb)同步属性到模型中其它所有同名的表。不过,这里我们直接遍历了所有模型中的表全改了一遍,就不需要同步了。

运行结果:

回到模型图,刷新可见效果:

PAS就先说到这。

 

如需了解EZDML脚本中的对象及其关系等详细内容,请参考《EZDML脚本配置》。