MidCOM 2.5 Datamanager Rewrite Main Object Structure
This is a MidCOM 2.5 Development Technical Note covering the Datamanger Rewrite. Everything written here is subject to change without notice.
Class Tree overview
- Data Type
- Encaspulates data type operations, including conversion to and from different storage formats.
- Implements read and write operations on the encaspulated data
- Data Manager
- Main Data IO class, manages basic IO operations.
- Works with types
- Can operate standalone, that is without widgets or form management code or even operation code, in case of purely programmatic processing.
- Abstracts data storage away from the datatypes, doing any neccessary conversion (for example automatic serialization of complex types).
- Supplies storage operations which are independant of a concrete Midgard object.
- Form Manager
- Manages widgets and form/view rendering
- Widget
- Takes over actual rendering, both in view and form modes
- Has hooks to allow for customization through extensions
- Dataschema
- The dataschema is now encaspulated in its own object, including operations on the schema information.
- Operation control class
- Brings everything together, serves as a base class for customizing behavoir.
- Links extensions like AJAX into the system
- Not required for simple data I/O operation (f.x. d.l.taviewer onsite processing), only geared for more intelligent views (like ais taviewer processing).
- Special control class for operation without any direct data storage, for example for mailback forms etc. (Perhaps this can be put into the Form manager directly, using it standalone.)
- Special tools for multi-request editing, using temporary storage objects to allow for undo.
Data Types
Data Types are one of the basic building blocks of the new Datamanger system. They encaspulate the operations neccessary to convert between various representations of the type applicable for storage, export/import and so on.
The types should support the following formats apart from the "operation format", the runtime state of the type:
- Storage format (any basic serializable PHP type or structure)
- CSV representation
- XML representation (Milestone 2)
- ... ?
This should give us an API looking like this, be aware that we use default constructors everywhere which call a _constructor function for easier subclassing:
class datatype
{
function _constructur($config) {}
function _constructed() {}
function convert_to_csv() { return $value; }
function convert_from_csv ($source) {}
function convert_to_storage() { return $value; }
function convert_from_storage ($source) {}
function convert_to_xml() { return $value; }
function convert_from_xml ($source) {}
function validate() { return $bool; }
function get_validation_error_message() { return $string; }
// various members representing the object values
// none of them is mandatory,as the conversion functions
// are used for interfacing.
}
Validation
Validation is a very special thing, because it is implicitly checked on most I/O operations. The important point here is that the value of a type can be invalid in itself, but if you request storage conversion, it will fail due to the invalid data (essentially skipping data storage for that specific field).
Datamanager
This part of the class hierarchy encaspulates the actual data I/O operations. It is important to understand that the actual datatypes used here are fully independant from the storage location, the corresponding I/O code is located in the central class, not in the types itself. This allows usage of the types not only in the actual data I/O but also in the Form Manager to encaspulate the data to be shown.
During runtime, the state of the storage object is represented by the type members, unless an explicit save is called, the storage object is not modified. To faciliate more flexible editing, storage objects are not limited to simple Midgard objects. Instead, an encaspulating class takes care of this operations:
Storage backend implementations
Two kinds of storage backends are available: Persistent and Temporary. Concerning the datamanger, both are interchangeable, the operational classes controlling the object hierarchy make the decision when to use what.
- Temporary
- PHP Session based storage (with DB additions for attachments, see below)
- Persistent
- MidCOM DBA object storage
- XML File (Milestone 2)
Special care has to be taken with attachments: Attachments have to be usable even within an editing cycle, without breaking GUIDs later. To faciliate this, the temporary storage systems will have a Midgard Table accociated with them, much like a session table, on which the attachment created during an (yet unfinished) object cycle are attached to. When the editing cycle finishes successfully, those attachments can then be "moved" to the main object by just changing their parent fields. A cron job will have to take care of cleaning them up on a regular basis.
Other then that the operation of a storage backend should be quite simple for a general start:
class storage_backend
{
function storage_backend ($object) {}
function store ($types {}
function load () { return $types }
// Attachment handling ?
}
TODO: Attachment handling interface? Perhaps some list/get/create calls matching the basic DBA API idea.
Datamanager core class
This class encaspulates the whole editing process by abstracting temporary and persistant storages from another. Its basic task is loading information from persistant objects and create temporary copies of it when and where neccessary.
API draft:
class datamanager
{
function datamanager ($schemadb) {}
function set_schema ($name) {}
function set_storage ($object) {}
function autoset_storage ($object) {}
function save() {}
var $types
}
When you enter editing mode, the system spawns everything neccessary for non-destructive, multi-request editing, while
Form Manager and Widget Library
The Form Manager along with its widgets encaspulates the whole process of automated user interface building. The Form manager serves as an entry point into the system, which is designed to work on top of a Data Manager class instance. Interaction on the large scale is controlled by the main operation class (see below).
Form Manager
The Form Manager's main purpose is to render a single form. (Note: multi-page froms are built using the operation classes.) So its main purpose is to create and prepare the widget list, and post process them where neccessary.
Special care has to be taken regarding validation. Validation is done prior to form rendering, so that any invalid values can be detected and marked accordingly in the form.
class form_manager
{
function form_managar(&$datamanager) {}
function show_view() {}
function show_form() {}
var $_widgets
}
Widgets
A widget is always based on a type (which it has to support explicitly). A widget knows how to draw the type it contains, both in view and edit modes, the caller decides on this. Widgets have two important properties concerning extensibility: First, the widget system forms a composite hierarchy (see Composite pattern), to make it easy to build composite widgets out of existing ones. Second, probably much more important, widgets can be extended using a decorator (see Decorator pattern), this point probably being the more important one.
class widget
{
function widget (&$type) {}
function draw_form_element() {}
function draw_view() {}
function draw_element_start() {}
function draw_element_ent() {}
}
When implementing decorators or composites, it should be possible to build a common base class to derive them from, so that it is easier to build them. (Even though it should actually not be strictly neccessary.)
Operation class
This is the main class, modeled after the Facade pattern. It abstracts most common processing tasks away and allows for deriving of it, so that you can easily customize its behavoir through event handlers.
class datamanager_operation
{
function datamanager_operation() {}
function set_schema ($schema) {}
function set_persistant_storage ($object) {}
function begin_editing() {}
function finish_editing ($cancel = false) {}
function process_form() {}
function _on_saved() {}
function _on_created() {}
function _on_cancelled() {}
function _on_validation_error() {}
// More event handlers as appropriate.
}
