从 PHP 生成 Javascript/ExtJS 代码最容易维护的方法是什么?

发布于 2024-10-14 06:37:32 字数 6152 浏览 2 评论 0原文

我正在创建一个 PHP 框架,它允许 PHP 开发人员仅使用 PHP 类创建 ExtJS 前端,例如创建网格如下所示:

$grid_import = new Backend_Layout_Grid('smart_worksheets');
$grid_import->set_width(1300);
$grid_import->set_rows_selectable(true);
$grid_import->set_title(__('backend.application.import.grid.title'));
$grid_import->set_margin('10px'); //CSS syntax, e.g. also "10px 0 0 0"
$grid_import->add_column(array('id_code'=>'name', 'label'=> __('backend.application.import.worksheetstoimport'), 'width'=>'300'));
$grid_import->add_column(array('id_code'=>'kind', 'label'=> __('backend.application.import.kind'), 'width'=>'50'));
$grid_import->add_column(array('id_code'=>'file_size', 'label'=> __('backend.application.import.sizebyte'), 'datatype' => 'int'));
$grid_import->add_column(array('id_code'=>'when_file_copied', 'label'=> __('backend.application.import.whenfilecopied'), 'datatype' => 'datetime', 'width'=>'150'));
$grid_import->add_column(array('id_code'=>'table_name', 'label'=> __('backend.application.import.mysqltablename'), 'width'=>'300'));
$grid_import->add_column(array('id_code'=>'when_table_created', 'label'=> __('backend.application.import.whentablecreated'), 'width'=>'160'));
$grid_import->add_column(array('id_code'=>'status', 'label'=> __('backend.application.import.status'), 'width'=>'300'));

$grid_import->set_doubleclick_target_uri('backend/application/importmanager/single', 0);

if (count($smart_worksheets) > 0)
{
    $row_index = 0;
    foreach ($smart_worksheets as $smart_worksheet)
    {
        $show_row = array(
            'name' => $smart_worksheet['name'],
            'kind' => $smart_worksheet['kind'],
            'file_size' => $smart_worksheet['file_size'],
            'when_file_copied' => $smart_worksheet['when_file_copied'],
            'table_name' => $smart_worksheet['table_name'],
            'when_table_created' => __($smart_worksheet['when_table_created']),
            'status' => __($smart_worksheet['status'])
        );
        $grid_import->add_row($show_row);
        if(in_array($smart_worksheet['status'], array('backend.application.import.status.needtoimport', 'backend.application.import.status.needtoreimport'))) {
            $grid_import->add_row_format($row_index, Backend_Layout_Grid::ROW_FORMAT_RED);
        }
        if(in_array($smart_worksheet['status'], array('backend.application.import.status.isuptodate'))) {
            $grid_import->add_row_format($row_index, Backend_Layout_Grid::ROW_FORMAT_GREEN);
        }

        if(intval($smart_worksheet['file_size']) > 4000000 AND (in_array($smart_worksheet['kind'], array('XLS','XLSX'))))
        {
            $grid_import->add_row_format($row_index, Backend_Layout_Grid::ROW_FORMAT_GRAY);
        }

        $row_index++;
    }
}

Backend_Layout_Window::instance()->add_item($grid_import);

到目前为止它运行良好,但因为我只是逐行输出 javascript 代码,我在类中构建的功能越多,构建原始 Javascript 文本的 if/then 逻辑就越复杂,这是生成 Javascript 代码的典型方法

public function render_main_code_block()
{
    $retval = '';

    $retval .= $this->render_data_variable();
    $retval .= $this->render_array_reader_block();
    $retval .= $this->render_grid_panel_block();


    if($this->rows_selectable)
    {
        $retval .= self::render_line("````}),");
        $retval .= self::render_line("````sm: sm,");
    }

    $retval .= self::render_line("````viewConfig: {");


    if ($this->percentage_columns)
    {
        $retval .= self::render_line("``````forceFit: true,"); // true = percentage column width (add up to 100)
    }
    else
    {
        $retval .= self::render_line("``````forceFit: false,");
    }

    $retval .= self::render_line("``````getRowClass: function(record, rowIndex, rp, ds){");
    if (count($this->row_formats) > 0)
    {
        foreach ($this->row_formats as $row_index => $row_format)
        {
            $retval .= self::render_line("````````if(rowIndex == ".$row_index."){");
            $retval .= self::render_line("``````````return '".$row_format."';");
            $retval .= self::render_line("````````}");
        }
    }
    $retval .= self::render_line("````````return '';");
    $retval .= self::render_line("``````}");
    $retval .= self::render_line("````},");

    $retval .= self::render_line("````title: '$this->title',");
    if ( ! is_null($this->width))
    {
        $retval .= self::render_line("````width: $this->width,");
    }
    $retval .= $this->render_double_click_handler();
    $retval .= self::render_line("````autoHeight: true,");
    $retval .= self::render_line("````frame: true");
    $retval .= self::render_line("``});");
    $retval .= self::render_line("");

    $retval .= self::render_line("``replaceComponentContent(targetRegion, ".$this->script_variable_name.");");
    $retval .= self::render_line("``".$this->script_variable_name.".getSelectionModel().selectFirstRow();");


    // for word wrapping in columns
    $retval .= self::render_line("``function columnWrap(val){");
    $retval .= self::render_line("````return '<div style=\"white-space:normal !important;\">'+ val +'</div>';");
    $retval .= self::render_line("``}");

    return $retval;
}

:使此代码变得过于复杂而难以维护的原因是:

  • 一个简单的事实是,在属性列表中,只有最后一项没有逗号。到目前为止,我已经能够将所需的属性放在列表末尾,以便始终包含不带逗号的项目。

在此处输入图像描述

  • 和某些功能(例如是否向网格添加复选框)需要在 up 中插入代码到 javascript 代码中的四个不同位置,我可以想象有些功能需要重组现有代码,因此如果打开该功能,则周围的代码需要包含在对象

所以在某些时候我想重构我创建Javascript的方式。

我目前只看到一个选项,即创建一个 CodeFactory 类,其中包含例如 ExtJsVariable ,我将递归地添加包含对象本身的对象,对象的类型可以是 simpleArrayanonymousFunction 等,然后创建代码,我将输出 $codeFactory->render()这将为我提供基于其所有内部对象的代码:

在此处输入图像描述

还有其他选项或我可以使用代码生成框架来使 PHP 生成的 Javascript/ExtJS 代码更易于维护和更直接吗?

I'm creating a PHP framework which allows a PHP developer to create an ExtJS front end with PHP classes only, e.g. creating a grid looks like this:

$grid_import = new Backend_Layout_Grid('smart_worksheets');
$grid_import->set_width(1300);
$grid_import->set_rows_selectable(true);
$grid_import->set_title(__('backend.application.import.grid.title'));
$grid_import->set_margin('10px'); //CSS syntax, e.g. also "10px 0 0 0"
$grid_import->add_column(array('id_code'=>'name', 'label'=> __('backend.application.import.worksheetstoimport'), 'width'=>'300'));
$grid_import->add_column(array('id_code'=>'kind', 'label'=> __('backend.application.import.kind'), 'width'=>'50'));
$grid_import->add_column(array('id_code'=>'file_size', 'label'=> __('backend.application.import.sizebyte'), 'datatype' => 'int'));
$grid_import->add_column(array('id_code'=>'when_file_copied', 'label'=> __('backend.application.import.whenfilecopied'), 'datatype' => 'datetime', 'width'=>'150'));
$grid_import->add_column(array('id_code'=>'table_name', 'label'=> __('backend.application.import.mysqltablename'), 'width'=>'300'));
$grid_import->add_column(array('id_code'=>'when_table_created', 'label'=> __('backend.application.import.whentablecreated'), 'width'=>'160'));
$grid_import->add_column(array('id_code'=>'status', 'label'=> __('backend.application.import.status'), 'width'=>'300'));

$grid_import->set_doubleclick_target_uri('backend/application/importmanager/single', 0);

if (count($smart_worksheets) > 0)
{
    $row_index = 0;
    foreach ($smart_worksheets as $smart_worksheet)
    {
        $show_row = array(
            'name' => $smart_worksheet['name'],
            'kind' => $smart_worksheet['kind'],
            'file_size' => $smart_worksheet['file_size'],
            'when_file_copied' => $smart_worksheet['when_file_copied'],
            'table_name' => $smart_worksheet['table_name'],
            'when_table_created' => __($smart_worksheet['when_table_created']),
            'status' => __($smart_worksheet['status'])
        );
        $grid_import->add_row($show_row);
        if(in_array($smart_worksheet['status'], array('backend.application.import.status.needtoimport', 'backend.application.import.status.needtoreimport'))) {
            $grid_import->add_row_format($row_index, Backend_Layout_Grid::ROW_FORMAT_RED);
        }
        if(in_array($smart_worksheet['status'], array('backend.application.import.status.isuptodate'))) {
            $grid_import->add_row_format($row_index, Backend_Layout_Grid::ROW_FORMAT_GREEN);
        }

        if(intval($smart_worksheet['file_size']) > 4000000 AND (in_array($smart_worksheet['kind'], array('XLS','XLSX'))))
        {
            $grid_import->add_row_format($row_index, Backend_Layout_Grid::ROW_FORMAT_GRAY);
        }

        $row_index++;
    }
}

Backend_Layout_Window::instance()->add_item($grid_import);

It works well so far, but since I am just outputting javascript code line-by-line, the more features I build into the class, the more complex the if/then logic gets in building the raw Javascript text, here is a typical method which generates the Javascript code:

public function render_main_code_block()
{
    $retval = '';

    $retval .= $this->render_data_variable();
    $retval .= $this->render_array_reader_block();
    $retval .= $this->render_grid_panel_block();


    if($this->rows_selectable)
    {
        $retval .= self::render_line("````}),");
        $retval .= self::render_line("````sm: sm,");
    }

    $retval .= self::render_line("````viewConfig: {");


    if ($this->percentage_columns)
    {
        $retval .= self::render_line("``````forceFit: true,"); // true = percentage column width (add up to 100)
    }
    else
    {
        $retval .= self::render_line("``````forceFit: false,");
    }

    $retval .= self::render_line("``````getRowClass: function(record, rowIndex, rp, ds){");
    if (count($this->row_formats) > 0)
    {
        foreach ($this->row_formats as $row_index => $row_format)
        {
            $retval .= self::render_line("````````if(rowIndex == ".$row_index."){");
            $retval .= self::render_line("``````````return '".$row_format."';");
            $retval .= self::render_line("````````}");
        }
    }
    $retval .= self::render_line("````````return '';");
    $retval .= self::render_line("``````}");
    $retval .= self::render_line("````},");

    $retval .= self::render_line("````title: '$this->title',");
    if ( ! is_null($this->width))
    {
        $retval .= self::render_line("````width: $this->width,");
    }
    $retval .= $this->render_double_click_handler();
    $retval .= self::render_line("````autoHeight: true,");
    $retval .= self::render_line("````frame: true");
    $retval .= self::render_line("``});");
    $retval .= self::render_line("");

    $retval .= self::render_line("``replaceComponentContent(targetRegion, ".$this->script_variable_name.");");
    $retval .= self::render_line("``".$this->script_variable_name.".getSelectionModel().selectFirstRow();");


    // for word wrapping in columns
    $retval .= self::render_line("``function columnWrap(val){");
    $retval .= self::render_line("````return '<div style=\"white-space:normal !important;\">'+ val +'</div>';");
    $retval .= self::render_line("``}");

    return $retval;
}

Some particular issues which is starting to make this code too complex to maintain is:

  • the simple fact that in lists of properties, only the last item does not have a comma. So far, I have been able to put have required properties at the end of the list so that the item without the comma is always included.

enter image description here

  • and some features (such as adding checkboxes to a grid or not) require code insertions in up to four different places in the javascript code, and I can imagine there are features which will require the existing code to be restructured, so if the feature is turned on, the surrounding code needs to be e.g. contained within another section of the object

So at some point I want to refactor the way I create the Javascript.

I currently see only one option really, to create e.g. a CodeFactory class which contains e.g. ExtJsVariable to which I add objects which contain objects themselves recursively, the objects can be of type e.g. simpleArray or anonymousFunction etc, and then to create the code I would output $codeFactory->render() which would give me the code based on all of its internal objects:

enter image description here

What are other options or code generation frameworks I could use to make the generation of this Javascript/ExtJS code from PHP more maintainable and straightforward?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

挽心 2024-10-21 06:37:32

最好的方法是尽可能避免动态生成 JS,并将所有 Javascript 保留在静态 JS 文件中(很可能是您的情况下的工厂、构建器)。并向它们提供从服务器生成的 JSON 数据。这极大地改善了结构,并使您的应用程序更易于维护。您可能想阅读有关工厂和构建器模式的内容。

The best way is to avoid JS generation dynamically as much as possible and keep all the Javascript in static JS files (factories, builders in your case most probably). And feed them with JSON data generated from the server. That seriously improve the structure and will make your application much easier to maintain. You may want to read about Factory and Builder patterns.

南巷近海 2024-10-21 06:37:32

我会提出一些建议,因为我也做了一些这样的事情。

首先,尽可能多地进入图书馆课程
其次,您可以创建一个 PHP 数据结构并使用 json_encode 将其转换为 JSON

最后保持简单!

I would make a few suggestions, as I have done some of this as well.

First of all move as much as you can into library classes
Second you can create a PHP data structure and turn it into a JSON with json_encode

And finally Keep it simple!

橙味迷妹 2024-10-21 06:37:32

事实上,我一直在经历类似的方式从 PHP,或者具体来说,从 CakePHP 创建 ExtJS 代码。

在我的公司,我负责创建应用程序框架,这将加快应用程序的生产速度,所以我实际上推出了我们的第一代框架,这与您的想法类似:通过PHP生成代码。

然后我很快意识到这对服务器来说相当繁重,而且速度很慢。您收到的每个请求都需要重新生成代码,这是不切实际的。不久后我又推出了第二代,具有缓存能力。生成的 JS 代码被缓存到静态文件中,并在需要时动态包含。缓存的原因是因为大多数时候JS代码都是静态的,变化的是内容、存储等。

所以这个方法工作了几个月,直到我最终意识到这不是正确的方法,例如,

  • TreePanel 不能通过这种方式生成,尤其是动态菜单。
  • ACL 无法干扰缓存的代码
  • 网格中不能有动态列
  • 我的框架设计得很糟糕,我需要在看到结果之前缓存代码 - 开发速度慢。
  • 维护困难,调用困难。添加一项配置或删除一项配置等简单的事情可能会很乏味。就像使用FormHelper来完成简单的事情是那么的乏味一样。

所以然后我完全重写我的代码库,重新设计一切,直到我想出这个当前漂亮的 ExtJS + CakePHP 框架,其中:

  • 创建了通用 GridPanel/Store/View/Pagination/Editor 组件,并且只需一些简单的配置,您就可以得到一个完整的 REST 网格,连接到 Cake 的一个控制器。在 Cake 中,您包含了专门设计的组件之一,只需几行代码即可创建一个完整的工作 REST 网格。
  • 特别设计的 Viewport 提供类似的外观和感觉,特别设计的 TreePanel(菜单)、TabPanel(内容)
  • 和许多其他通用的、有用的组件打包在一起,如 Wizard、FieldEditor、UserImport... 等。
  • 而且因为它们都是可扩展的ExtJS 组件,您可以使用它、扩展它、完全自定义它以达到您想要的任何效果。

很抱歉,如果您觉得这听起来不太合适,但就我个人而言,我已经经历过这个,并且我对从 PHP 生成代码的结果感到非常不满意。基本上你最终会使整个过程变得复杂,或者减慢速度。

顺便说一句,对不起我的英语不好。感谢您阅读本文。

我不能分享我的代码,也不能开源它,因为它是公司财产的一部分。

In fact, I have been going through similar way of creating ExtJS code from PHP, or specifically, from CakePHP.

In my company, I am responsible on creating application framework which will speed up application production, so I actually come out with our first generation framework, which is similar to your idea: Generating codes through PHP.

Then quickly I realize that is pretty heavy for the server, and slow. Each request you get you need to regenerate the codes, which is impractical. Shortly after I come out with the second generation, with caching ability. Generated JS codes get cached into static files, and being included dynamically when needed. Reason for caching is because most of the time the JS codes are static, changes are the content, store etc.

So this method works for couple of months, until I finally realize this is not the correct way, for example,

  • TreePanel cannot be generated through this way, especially with dynamic menu.
  • ACL cannot interfering the cached code
  • Can't have dynamic columns in the grid
  • My framework is designed badly that I need to cache the codes before seeing the result - slow development.
  • Hard to maintain, hard to call. Simple things like adding one config or remove one config can be tedious. Just like it's so tedious to use FormHelper to complete simple stuff.

So then I completely rewrite my code base, redesign everything, until I come up with this current beautiful ExtJS + CakePHP framework, which:

  • Generic GridPanel/Store/View/Pagination/Editor components was created, and with just some simple configuration, you get a complete REST grid that hook up to one of Cake's controller. In Cake you include the one of the specially designed component, and just with few lines of code you created a full working REST grid.
  • Specially designed Viewport which provides similar look and feel, with specially designed TreePanel (Menu), TabPanel (Contents)
  • And many other generic, useful components being packed together, like Wizard, FieldEditor, UserImport... etc.
  • And because they are all extendable ExtJS components, you can use it, extend it, fully customize it to whatever you want.

Sorry if this doesn't sound right to you, but personally I have gone through this, and I am really unhappy with the result of generating codes from PHP. You basically end up complicates the whole process, or slow thing down.

Sorry for my bad english by the way. And thanks for reading this.

And I cannot share my code nor open source it because it's part of company's property.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文