从 PHP 生成 Javascript/ExtJS 代码最容易维护的方法是什么?
我正在创建一个 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
,我将递归地添加包含对象本身的对象,对象的类型可以是 simpleArray
或 anonymousFunction
等,然后创建代码,我将输出 $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.
- 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:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
最好的方法是尽可能避免动态生成 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.
我会提出一些建议,因为我也做了一些这样的事情。
首先,尽可能多地进入图书馆课程
其次,您可以创建一个 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!
事实上,我一直在经历类似的方式从 PHP,或者具体来说,从 CakePHP 创建 ExtJS 代码。
在我的公司,我负责创建应用程序框架,这将加快应用程序的生产速度,所以我实际上推出了我们的第一代框架,这与您的想法类似:通过PHP生成代码。
然后我很快意识到这对服务器来说相当繁重,而且速度很慢。您收到的每个请求都需要重新生成代码,这是不切实际的。不久后我又推出了第二代,具有缓存能力。生成的 JS 代码被缓存到静态文件中,并在需要时动态包含。缓存的原因是因为大多数时候JS代码都是静态的,变化的是内容、存储等。
所以这个方法工作了几个月,直到我最终意识到这不是正确的方法,例如,
TreePanel
不能通过这种方式生成,尤其是动态菜单。FormHelper
来完成简单的事情是那么的乏味一样。所以然后我完全重写我的代码库,重新设计一切,直到我想出这个当前漂亮的 ExtJS + CakePHP 框架,其中:
很抱歉,如果您觉得这听起来不太合适,但就我个人而言,我已经经历过这个,并且我对从 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.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:
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.