The Form-Class Design Guideline
To state the obvious, forms must be visually manifest as HTML and submissions to them must be processed and responded to. Some websites make a total mess of the browser address bar by having form submissions make HTTP GET requests to the server. Others keep the address bar clean by using HTTP POST requests. Either way the submitted data is processed by a form handler on the server that is expecting the form. Exactly which form handler (or parts of a general form handler) will actually run, is determined by the server on the basis of the URL to which the form is submitted. This URL being that given in the form HTML, either in the tag of the form action button or in the tag for the form as a whole.
In many websites the form HTML and any validation JavaScript is only a part of a .html file that is faithfully regurgitated by the server when requested. Values are ferried between forms and the database by a plethora of scripts in languages such as PHP. In these implementations there is no formal regime to tie form components to database components. It is up to the developer to get it right. With Dissemino there is such a regime and it pays to go along with it.
Central to the design of Dissemino is the relationship between data classes and forms. The principle purpose of hierarchical data classes that allow members to hold multiple values and sub-class instances, is to store descriptions of real world objects. Since the purpose of forms is to create and edit such descriptions, it follows that forms should be closely matched to a data class. Ideally, each form submission, would only create or edit a single object of the applicable data class and if small, objects of any sub-classs. This ideal is known as the form-class design guideline.
Within the guideline forms can have fields that are unrelated to any data class members, e.g. an email verification code for user registration. And if there are too many members in a class to fit in a single form, these can be spread across multiple forms. Also permitted are validation lists based on a single member of an alien data class. If in an accounting system we have a data class of 'income/expenditure categories' with members of name and description, then a form for entering transactions (yet another data class) could present a select list of these categories by name. As long as each form submission does not create or edit more than a single object (of the same or different data class), then the form lays within the guideline. If a form falls outside the guideline, it can pay to question the data model. As a general rule, the only repositories that will be required are those storing real life objects, so the only classes needed are those repository natives and any sub-classes thereof. There can be circumstances where it will make sense to ignore the guideline, but it is not something that should be done without a very good reason.
The form-class design guideline leads directly to the notion of forms having a native data class. It is not compulsory to set a native data class for a form but doing so simplifies matters. When defining forms, field names can be directly matched to members of the native data class so it is only necessary to specify the data class where fields address members of alien data classes. The form handlers will inherit the native data class to the same effect, although if the native data class is not set in the form it can be set separately in the form handler. Shorthand however, was not the objective. Of greater importance is the opposite notion, that data classes imply forms. In Dissemino, data classes can act as input for an automatic form and form handler generation process. These are known as the default forms and form handlers for the data class. With basic form HTML and little functionality beyond creating and editing class instances, these are crude devices. They are nonetheless, an effective aid to rapid website development.
Form Virtualization
Under Dissemino pages and articles (partial pages) are formally defined and registered real entities. Forms by contrast, are virtual and abstract so do not as such, exist. Instead, there are three form related entities; form definitions, form references and form handlers. These three work together as follows:-
Form definition | Determines what the form will look like in terms of fields, form action buttons and supportive HTML. It states which form handler will be invoked in the case of each form action button (usually one but can be more). It also states the form native data class, which will also apply to the handlers. It does not however, state either the repository or the data class context. |
Form references | These are the points of use so they state the repository used in submissions and the class context. The references also generate the form HTML. |
Form handlers | These process the submissions using the class stated in form defintion and the repositories and class context stated in the form reference. |
To be clear the class context (introduced in "ID Allocation", article 2.3 "Datum and Data Objects in an Application"), is simply the use of different ids for the same data class in order to state how it is deployed - either as native or as a sub-class. It is specified textually as either a pure class name or of the form "class_name.member_name". Class context does not detract the three form entities from doing as their names suggest. A form once defined, however it is subsequently referenced will always appear the same and have the same buttons which will POST the form data through to the same form handlers.
Respectively, the three form entities are manifest internally as C++ class instances of hdsFormdef, hdsFormref and hdsFormhdl, created during the config read process in response to <xformDef>, <xformRef> and <xformHdl> tags. There is only one minor departure from this theme. The <xformDef> tag can either be in-situ (within a page or article definition), or be independent. Because it is the hdsFormref that generates the HTML an in-situ <xformDef> tag creates both a hdsFormdef and a hdsFormref.
This demarcation sometimes raises eyebrows. It is tempting to think that as the form definitions contain everything needed to generate form HTML, the task of so doing ought to fall to the hdsFormdef class. Instead hdsFormref generates the HTML in spite of not holding any of the form details itself and needing a pointer back to the applicable hdsFormdef to get this information. The form definitions are essentially inert. They state much but do little. Form virtualization separates the abstract from the real. The relationship between form definitions and for references is a akin to that between a class and an instance of the class. The former is theory, the latter is fact.
Form virtualization makes no practical difference in the very common case where a form is defined within the only page that will display it. In earlier versions of Dissemino, forms had to be defined in-situ. There were no hdsFormdef or hdsFormref, only a form class (hdsForm) created in response to an <xform> tag - which both defined the form and generated the HTML. The Forms worked but with an earlier and inferior database regime. There was a 1:1 relationship beween data classes and repositories and sub-class objects were always held in a separate repository. The single object container hdbObject was likewise so there had to be an instance for each sub-class object. Articles did not support forms lest this lead to illegally nested <form> tags in the generated HTML.
Numerous cases arose in which form definitions were being repeated with only minor differences, leading to confusion and error. Several factors pointed to form virtualization. What actually forced the issue however, was the introduction of the data class default forms and form handlers. As these defaults are generated directly from the data class definitions, and not within any page or article, form references were needed to import the defaults into pages and articles. This necessitated HTML generation by form reference, as otherwise no HTML for the defaults would ever appear. From this point on, it made no sense for there to be two different approachs to form HTML generation so the regime was rationalized so that in all cases, the HTML would be generated by form references.
Form Management
In Dissemino, forms are managed by maps in the hdsApp (HadronZoo Dissemino Application) class, mirroring the approach of the HDB which manages data object model entities in maps in the hdbADP (Application Delta Profile) class. One such map is hdsApp::m_FormDefs, a 1:1 map between form definition names and form definitions. Upon encountering an <xformDef> tag, the config read process (assuming the proposed name does not yet exist), creates a hdsFormdef and adds it to m_FormDefs. <xformDef> has only two subtags of significance, <xfield> to add fields and <xformBut> to add form action buttons. All other <xformDef> subtags serve only to define the supportive HTML. Each form action button in the form definition must name a form handler. The form handler itself will be defined later since in the configs, form definitions must always precede form handler definitions. Form handler names are added to another hdsApp 1:1 map, m_FormHdls.
Form references, regardless of whether they are generated by an <xformDef> or a <xformRef> tag, use three hdsMaps namely;
m_FormUrl2Hdl | 1:1 Form action URLs to form handlers (used to invoke form handlers) |
m_FormUrl2Ref | 1:1 Form action URLs to form references (used to obtain form handler op context) |
m_FormRef2Url | 1:many Form reference pointers to form action URLs (used by hdsButton::Generate()) |
Note that ULRs are required to be unique only in so far as there must be a separate URL for every combination of form handler and class context incident in the configs. Where there are two or more form references to the same form definition and using the same class context, the URLs can be different but there is no need for them to be.
Form Operation
Consider the statement in the opening paragraph, "Exactly which form handler (or parts of a general form handler) will actually run, is determined by the server on the basis of the URL to which the form is submitted. This being that given in the form HTML, either in the tag of the form action button or in the tag for the form as a whole". To be more specific, there is no general form handler in Dissemino and URL first identifies the form reference, not the form handler. All form submissions are by POST and none by GET. All HTTP requests are handled by hdsApp::ProcHTTP(). This uses the URL to look up the form reference in hdsApp::m_FormUrl2Ref and the handler name in hdsApp::m_FormUrl2Hdl. The handler name is then used to look up the form handler in hdsApp::m_FormHdls. The function hdsApp::ProcForm() is then called with both form reference and handler. It is this function that executes the form handler which comprises a series of Dissemino executive commands (instances of hdsExec).
In order for form submissions to invoke the correct form handler in the correct class context, the generated form HTML must have the correct URL for the form reference. This can be in the <form> tag itself or in the <input> tag of the applicable form action button. Page/article HTML generation is a matter of iterating the array of visual entities (hdsVE derivatives), for the page/article and calling the Generate function on each one. The visual entities equate to HTML tags and each implements Generate in accordance with its own requirements. The array is only the top level of visual entities in the page/article. Where these have subtags, Generate will recurse.
In this process, hdsFormref which produces the <form> tag, is just another visual entity. That hdsFormref::Generate only has the visual entities for the form body by means of a pointer back to the hdsFormdef is an aside. The thing to note is that the all important URL isn't going to be anywhere among them. Nor is the URL stored elsewhere in hdsFormref, only the class context is. Whether the URL is vested with the form or the button will depend on the configs but both hdsFormref::Generate and hdsButton::Generate must be capable of producing the URL. In either case the URL is looked up in the 1:many hdsApp map m_FormRef2Url using the pointer to the hdsFormref as key. The m_FormRef2Url map is 1:many because a form can have multiple form handlers and it is not compulsory for multiple form references to the same form definition in the same class context, to always use the same URLs.