The article discusses the concepts of creating an IDE and an interface designer using ExtJS and PHP. On the one hand, creating such editors is quite a rare task, on the other hand, concepts and techniques can be used to create various visual configurators.How to write your IDE with a built-in interface designer, how to do it quickly and with minimal effort? This question arose once in a project using a bunch of ExtJS and PHP. Last minute, growing queue of tasks. The list of tasks is updated daily with a huge number of input forms, tables and reports, all of which need to be processed, filtered and displayed for the user.
The project used an ORM, which clearly hinted at the possibility of automatically generating similar interfaces, but with many similarities, each form and report was unique, which required the ability to be quickly and easily configured.
')
A quick search was issued by Ext Designer (Sencha Architect) - an interesting and useful tool (Ext MVC did not exist yet). Ext Designer did not help to solve the tasks, but about everything in more detail.
At the same time, the PHP-Ext project was noticed - an interesting wrapper for ExtJS, written in PHP. It could be used to generate this endless stream of interfaces, but I wanted more: mix Ext Designer and PHP wrappers over ExtJS, so that you can learn how to “peep” into the database and build forms on the basis of tables. And even better based on the structure of ORM objects, because there are all the field names, their types and validators.
The search for such a tool was not successful.
What the IDE will consist of:
1 wrapper for ExtJS, which “can” generate component code;2 project file with a simple API for adding ExtJS wrapper elements;3 code collector that locates elements and caches the result;4 interface designers to simplify project customization, code editor;5 “buns” in the form of ready-made components for automation of routine tasks:5.1 non-obvious features of ExtJs inheritance for beginners;5.2 addition of editors;5.3 generating url for Ajax requests for two clicks;5.4 import of the structure from the database, automatic creation of forms;5.5 connection of external files and projects;5.6 interface localization;5.7 editors of events and methods;6 backend designer who translates front-end requests to API teams working with the project;8 generator projects.1 Wrap
Under this task, you will have to write your own ExtJS wrapper, adapted to the needs of the idea. Since the volume of the library is large enough, we will save time by separating the sets of properties of the library components from the description of their behavior. Not all ExtJs objects will be mirrored in PHP code. We describe classes of a limited number of objects such as Grid, Store, Window, etc. All other objects may not physically exist in the code, but be created based on the set of properties described in the hierarchy.
Thus, in a couple of days, the hierarchy of properties of the ExtJS components was recreated, while version 3 (several dozens of classes) was still available. Then it was possible, as necessary, to describe the behavior of the necessary components, the main ones were generated at the level of the json-config.We obtain a hierarchy of the following form (a piece of the diagram, the times of Zend_Framework 1):
Ext_Object and its heirs are responsible for the behavior of the component;
Ext_Config is responsible for storing, validating and serializing properties;
Ext_Property and its heirs are responsible for describing the characteristics of a particular ExtJS component (classes with public properties, to some extent repeating the hierarchy of the ExtJs library).
Ext_Virtual - class. Objects of this class are created by the factory if there is no direct descendant of Ext_Object that describes the behavior of the component. It is created based on the description of the Ext_Property_xxx properties. It differs in that it takes the name of the class to emulate, using the name it presents to the library components.
Ext_Object implements the __toString method, which returns the ExtJS code of the component. If necessary, this method can be overridden by successors. If the objects are nested into each other, at the time of casting to the string of the root object, the whole chain can easily be unpacked (turns into a string of Js code).
As practice has shown, this decision avoided a headache.2 project file
The interface on ExtJS consists of components, lined up in a chain and located in Layout.
It is necessary to somehow store the configuration of the component component links You can use XML or some other similar format as a configuration file.
In this case, each time the project is loaded, you will have to analyze the configuration and initialize the objects, which can take a long time. We need a simple, fast and easy format.
What if we declare the Designer_Project class, which would represent the project itself and have a simple API for adding elements, and the elements themselves would be stored in a tree structure (inside there would be an object working with a tree structure).
At that time, the
Tree class was already written, which worked quite quickly with tree structures, easily coping with the hierarchy of up to 25,000 - 30,000 nested elements in less than a second.
The object of such a project can be serialized and saved to disk. Moreover, it is possible to take a hash from the serialized string and reassemble the interface code only if there are changes. Reinitialization of the interface could use the file cache without loading the project for validation.
The main problem of Ext Designer at the time seemed to be that every time you change the code, you need to rebuild the application and publish the scripts.Designer_Project structure (project file):
- system description of containers (which classes can be moved, which can contain nested elements, etc.);
- settings of the current project (name, namespaces, connected files, etc.);
- API (set of methods for working with the project);
- Tree (element tree, project structure).
It is noteworthy that the component classes can be expanded, and the list of properties can grow. All this in most cases does not cause compatibility problems with the old format.
This is how the project file appeared. Along the way, several auxiliary classes are written, for example, the adapter Designer_Storage (what if we change our mind about storing projects in files). Several performance tests were conducted, the results were optimistic, the idea worked smartly. It is important to note that the project tree knows only about the nesting structure of the elements, but the objects themselves are not actually in each other. Designer / Project.php3 code collector
Since the Designer_Project class is a container with a simple API and doesn’t know what to do with its content, you will need an auxiliary mechanism that can correctly position the code of elements in the desired sequence - this is the Designer_Project_Code class. Perhaps the most difficult component by the number of different conditions and branches. Getting the project object as input should return the JS code for the interface. Recursively going through the project tree receives the component code and arranges the elements in the desired sequence. It is important to note that the component code itself provides a wrapper for ExtJS, the collector itself is engaged in arranging this code in the right sequence. It must determine which components should be declared first, get their code, and insert references into dependent components.
The code turned out to be quite complicated and confusing, refactored many times, with the advent of new features it became even more confusing.Over time, he acquired a tolerant look and structure. Designer / Project / Code.php
For JS objects that are extensions of the ExtJS base components, a trick was used to simplify the layout of nested elements. The childObjects property was created, which is a list of all nested elements, so it is very easy to access and link them to items.The principle of Designer_Project_Code in recursively traversing the structure of the project, finding related components and the correct positioning of the finite element code relative to each other.
The floor is done. A simplified version of the PHP-EXT wrapper has been created, which can be folded into a project and works much faster. Functionality is limited, but no one bothers to develop it.4 Designer
The time has come for the most interesting - the creation of an interface designer. Conceptually, it should be:
- a panel with a toolbar in which the list of components that can be placed in a project (buttons, forms, windows, panels) would be located;
- the main form that would display the results of rendering the project;
- component hierarchy (used by TreePanel);
- Property Editor (Property Grid).
At the moment, the designer has the following form (very different from the first):
1. project settings panel (loading, saving, switching the designer / code editor mode, etc.);
2. toolbar with a list of components that can be added to the project;
3. the panel displaying the project structure supports Drag & Drop moving elements; when an element is selected for it, an individual property settings panel is loaded;
4. component properties settings panel (contains additional event and method editing panels).
5. central panel (displays the result of the project rendering, in this screenshot - the localization editor). When a project is loaded, the server saves a copy of its object to the session, all manipulations with it. Thus, if the interface is dropped, you can reload the window, the changes will not be lost. The “Save” button resets the project to disk.
After making changes, the interface sends a request to the server, the required controller accepts the request, makes changes to the Designer_Project object. After successfully applying the changes, the designer requests a rebuild of JS.
The main designer panel, which is responsible for the location of elements in the project, is a tree with drag & drop support:
The tree requests a list of items from the server, which in turn extracts the structure from the project using the API. While dragging an item, a request is sent to the server with instructions on which component is being moved. The Designer_Project API contains a method for moving items around the tree. When clicking on a tree node (selecting the desired object), an itemSelected event is triggered, and the property bar of this component is displayed.
The interaction of the components of the editor occurs at the event level, individual parts and components often do not have an idea of ​​what is outside. Each element has a set of generated events for which the parent component is usually subscribed.An extended Property Grid component was used as a property editor, supplemented by methods of communicating with the server (requests a list of fields, sends changes to properties, triggers events).
In our case, this component was called designer.properties.PanelOne property panel is indispensable; in some cases, the ability to configure additional properties, which are editors and windows, is required. If it is necessary to expand the list of settings for this type of objects, an individual editor, inherited from
designer.properties.Panel , is assigned.
The initial capabilities of the designer failed to solve all the tasks, so the file was attached to the project “actionJs” (a file with JavaScript code, used for what cannot be done with standard tools, connects after the JS project).
Codemirror.net is used as a code editor.
The first versions of the designer could simply and quickly build the interface, everything else fell on the shoulders of the developer. In later versions, events appeared in the designer, the possibility of expanding objects, adding methods, etc.How to display the result? You can render projects directly in the open page DOM, it is very fast, the rebuild lags are almost imperceptible, after a element is thrown, a fraction of a second passes through the tree before the interface is rebuilt. This solution has one serious problem, if something goes wrong in the project under development (the property is set incorrectly or something else), JS errors will cause the collapse of the entire designer. The developed interface is better to transfer to the iframe, although this will slow down the response, but the collapse of the project code will not lead to global collapse. The iframe itself can be put in the central panel, if necessary, request an update of the content.
It would be nice if the elements could be thrown on the form and moved / moved directly inside the project being developed, as in all “adult” designers, but this question cannot be solved by simple terms. It was necessary to refuse this undertaking for a while, the time was running out.
Later, a mechanism of interaction between the main interface of the designer and the project being created appeared. You can move, stretch the columns of the tables, resize windows, all this is preserved. The principle of operation is quite simple - event handlers are added to the interface being developed during the design mode, which form a team for the designer and help to interact with the controller controllers, the external interface of the designer waits for the command and reacts when it appears. For example, when moving table columns, a request is sent to the serverside API and the main designer interface is notified.5 Buns in the form of ready-made components, automation of routine tasks
5.1 Unobvious inheritance features in ExtJs for beginners.
Before touching on the theme of the finalization of the components, I would like to draw attention to the features of inheritance that are not obvious to the beginner.
Ext.define('mypanel',{ extend:'Ext.Panel', someProperty:{ a:1,b:2,c:3 }, someProperty2:[1,2,3] }); Ext.define('mypanel2',{ extend:'mypanel' }); var a = Ext.create('mypanel'); var b = Ext.create('mypanel2'); b.someProperty.a = 100; b.someProperty2.push(100); console.log(a.someProperty); console.log(b.someProperty); console.log(a.someProperty2); console.log(b.someProperty2);
Object {a = 100, b = 2, c = 3}
Object {a = 100, b = 2, c = 3}
[1, 2, 3, 100]
[1, 2, 3, 100]
These features are important to remember, because it will save you a lot of nerves. This is not the only caveat, be careful.
5.2 Editors
We will override the editors of the basic properties, for example, we replace the store editor from the text field with a drop-down list of all the repositories created in the project (requested from the server, given to the Designer_Project API), as well as with other similar properties (layout, align, and many others).
5.3 Generating a url for Ajax requests for two clicks
Url in the system is dynamic, for example, the address of the panel can change, the controller switches. To this end, tokens were invented, replacing the real path, processed at the stage of code assembly.
To simplify the assignment of URLs (ajax-requests, proxy url), a component is written that analyzes the file and code structure. Reflection allows you to receive and analyze the list of available actions for controllers, now they do not need to write with your hands, just poke the mouse.
Principle of operation: the file system is scanned, the required class is selected, a list of methods is requested, the method comment is used as a description for the interface. The desired url is automatically registered in the property as a template.
A similar approach can be used to assign icons.
5.4 Importing a structure from a database, automatic form creation
How else to make life easier for the programmer? Of course, importing fields into forms, storages, tables based on the structure of the database or ORM.
Select the table / object from which we want to import fields. Based on its structure and data types, the system automatically inserts the necessary values ​​into the cells and fills the hierarchy of forms.
For example, for int is substituted Ext.form.field.Number, for varchar - Ext.form.field.Text, etc.
A fieldLabel is added along the way if it is a form field or a date format, if it is a storage field and other properties.
Now the tedious procedure for describing the elements takes a couple of clicks.
5.5 Connecting external files and projects
Why not give the opportunity to connect to the project external JS-files and other projects. We can create a project in which we will describe a specific component - the editor, later we will connect it where it is needed. The main thing is to separate the project namespaces so that the code generator places each project in its namespace; this is not particularly difficult.
All that was required was to add support for adding a list of files and projects to Designer_Project api, assigning namespaces. Designer_Project_Code for a couple of hours was trained to put the code inside the namespaces and recursively render projects.Gradually, the designer began to replenish with ready-made components such as editing windows, filters, fields for references to objects, lists of objects, and much more.
5.6 Interface Localization
For a long time, the problem of localizing the interface remained unsolved. It was not clear how to approach her decision. The whole project was based on simple solutions, I didn’t want to make a fuss, and there wasn’t much time for it. At one point it was found.
The platform itself used localization files that were regenerated when a change was made in the localization management interface. As a result, a certain JS object of the form existed on the page:
appLang = { yes:””, no:””, cancel: '', ... };
The idea is simple - to allow the developer to enter the Js code as values ​​of the string properties, for this the token “[js:]” was coined (during the generation of the code, properties with such a token were issued as js code).
It was:

Became possible:
Given that the localization management interface was already ready, it was a pleasure to work with it. The implementation of the decision took on the strength of 20 minutes. Now you can localize the project without changing the contents of the file.5.7 Event and Method Editors
The next breakthrough was the addition of the possibility of expanding objects, now you can add methods and events. The implementation of events is very similar to the implementation of properties. Similarly, methods were implemented. At this point, the system had a division into standard events described in the Ext wrapper, and events created by the user (methods could only be created by the user for “extended” (isExtended attribute) objects).
The designer began to generate a wave of clear, readable code.
After this, an obvious problem appears: events, methods and reactions are scattered around the elements, it is difficult to find where to edit them. Add to the designer separate tabs with lists of events and methods, grouped by objects. Request a list of objects from Designer_Project, display it as a Grid with grouping.
6 Backend
From the point of view of the backend, everything is quite simple, you can use any framework. Need a set of controllers and actions for manipulating the project. We start the application, load the designer’s project, describe the list of methods that access the Designer_Project API and perform various manipulations with the project. Of the features - you need a controller that can bring the assembled project to the interface and connect the necessary JS-files.
In our case, in addition, an action was created to assemble and minify the source code of the designer himself, this made it possible to speed up the loading of the editor.7 Project Generator
A generator is a template with a set of actions on a project file.
In simplified form:
Having a similar project structure, writing an ORM-based project generator was easy. During the day, several templates for generating standard interfaces were created, now a couple of clicks took to create a standard interface, the rest of the time was spent on non-standard design and refinement.
The implementation of the first version of the designer took 3 weeks of vacation - a rather short time for such a global goal.
The resulting profit:
- standard interfaces were generated by one click, further developed in the designer;
- significantly reduced the number of errors in the JS-code;
- young developers are easier to penetrate into ExtJS and the development of a complex project;
- modifying the interface has become much easier and more interesting, the elements moved with one mouse movement, without fear of forgetting to capture a piece of code and miss connectivity with another element;
- increased speed of product development and prototypes;
- such things as changing the name of a button or column, stopped calling bathert during the search for a piece of code with the initialization of the desired element;
- an interesting experience was gained in the development of its own development environment “on the knee”
- PHP development has acquired a completely new user-friendly form;
- I managed to learn a lot of the subtleties of ExtJS, which I hadn’t encountered before.
Why was all this necessary?
Over the years working with ExtJS, I got tired of writing the same thing. Initialize components, tune them, afraid to skip a comma or link something. This task, as it turned out, is quite easy to automate, in the vacant time you can do more interesting things.
Since the tool turned out to be very useful, there was a desire to share it with the community, to contribute to OpenSource. The designer and several other developments have been reworked, assembled into one DVelum platform, which has been developed for several years. The results can be found on the official website
dvelum.net