Open tn3rb opened 3 years ago
Everything looks perfect, though I didn't fully understand how the schema defined for Rule will be integrated with our form
.
I didn't fully understand how the schema defined for Rule will be integrated with our form.
It was still a work in progress and has been updated since you last saw it. There is now a table for recording what action should be taken when a set of rules passes; for example, displaying a form input when the answer for another input matches some value. Then there are a couple of intersection tables between Actions and other models called "Action Object" as well as between Actions and Rules called "Action Rule".
The Data Schema above does nothing on its own of course and only becomes functional within a framework that can utilize it. Ideally I would like to see things combined in a functional or OOP manner (depending on language) following established design patterns.
For server side code I plan to utilize the "Specification" pattern as well as Composites (or variations thereof) so that rule objects can be created from db records and then passed to a mediator along with the other data being tested.
So off the top of my head, maybe something like:
class ActionHandler
{
private $actions_model;
private $rules_manager;
public function executeActionsFor(Candidate $candidate)
{
$actions = $this->getActionsFor($candidate);
foreach($actions as $action) {
$rules = $this->rules_manager->getRulesForAction($action);
if ($candidate->satisfies($rules)) {
$action->execute();
}
}
}
public function getActionsFor($candidate)
{
return $this->actions_model->get(
[
'OBJ_ID' => $candidate->ID(),
'object' => $candidate->type(),
// real query would be more complex
]
);
}
}
/**
* mediator between model objects and specifications handler
*/
class Candidate
{
private $model_object;
private $specifications_handler;
public function __construct(EE_Base_Class $model_object)
{
$this->model_object = $model_object;
}
public function satisfies($rules) : bool
{
return $this->specifications_handler->satisfies(
$this->model_object,
$rules
);
}
}
For the client side code, things will obviously look quite different. We will be starting with the conditional form logic where most usage will be for displaying or hiding inputs based on the current user input value for another question. Ideally we should build some sort of specifications manager in JS that will operate similarly in that the persisted rule data can be dynamically transformed into typed objects that can then be handled accordingly in order to determine whether a particular action should be executed.
It may be tempting to wire up the rule data directly to its usage in the form system, but that will eventually result in duplication down the road as more of our presentation layer gets converted to React.
The new Registration Form Generator will be integrated within the EDTR instead of the existing legacy admin page.
It will either be added below the tickets list
OR...
<tangent>
we could add a new UI element that would basically be an additional vertical sidebar navigation on the left-hand side of the main content area. It would be 50-60px wide and normally only display icons but could expand and also show text on hover / focus. The icons would represent the different sections of the Event Editor, such as
</tangent>
The Registration Form Generator will allow event admins to create registration forms that are customized for each event as opposed to simply assigning question groups to either the primary or additional registrants. The existing Registration Form admin page will also need to be replaced with essentially the same UI we add to the EDTR but will be only for editing default questions and form sections that will be duplicated and added to new events upon creation.
The Registration Form Generator will need to consist of the following features:
initially displays registration form as it would appear on the frontend (kinda sorta)
form element (form input and/or form section) would have an "action menu" that appears on hover / focus
the action menu contains options such as "edit", "copy", "delete", and "reorder".
the form generator should also possess a sidebar that will consist of panels that can be opened or closed to display tools/options/etc similar to Gutenberg sidebar.
the first panel should always be open and consist of basic CRUD commands like "Save", "Cancel", and other critical details about the entire form such as name ???
the next panel should be titled "Form Sections" and have options for:
the next panel should be titled "Form Elements" and will include a list of form inputs and elements that can be added to the form, such as:
core/libraries/form_sections/inputs
form inputs listed in the sidebar can be dragged and dropped into the reg form to add that input type
This modal appears when a user clicks on the edit action button for a form section. It will need to contain controls for all the attributes that get saved to the Question Group table. For improved UI/UX these should be separated into different groups whose display is controlled by tabs (???). For example:
control | type | ??? |
---|---|---|
name | string | |
showName | bool | toggle control |
description | text area | |
showDesc | bool | toggle control |
why toggle controls? because admins may want to label the form sections and add notes that they do not want displayed to the public in the actual registration form
control | type | ??? |
---|---|---|
css class | string | |
custom css | text area |
control | type | ??? |
---|---|---|
action | select | show or hide this field if |
ALL or ANY | select | ALL (or ANY) of the following are true |
target | select | list of current form elements |
comparison | select | > < = etc |
value | text | |
delete rule | icon button | trash can icon after each rule |
add rule | button |
This modal appears when a user clicks on the edit action button for a form element. It will need to contain controls for all the attributes that get saved to the Question table. For improved UI/UX these should be separated into different groups whose display is controlled by tabs (???). For example:
control | type | ??? |
---|---|---|
admin label | string | |
display text | string | html label text |
placeholder | string | |
help text | string | |
adminOnly | bool | toggle control |
control | type | ??? |
---|---|---|
label css class | string | |
input css class | string | |
help text css class | string | |
custom css | text area |
we can add additional controls here for various options that correspond to css classes
for example, having a control to select field width with options like small
, medium
large
or 25%
50%
100%
which would result in specific css classes getting appended to the htmlClass
value saved to the db
changes depending on question type control | type | ??? |
---|---|---|
required | bool | toggle control |
requiredText | string | only displays if required is set to "on" |
max | int | for number or text inputs |
min | int | for number or text inputs |
control | type | ??? |
---|---|---|
action | select | show or hide this field if |
ALL or ANY | select | ALL (or ANY) of the following are true |
target | select | list of current form elements |
comparison | select | > < = etc |
value | text | |
delete rule | icon button | trash can icon after each rule |
add rule | button |
plz note that we can also add extra fields to the Question or Question Group models in order to hold additional data if need be so the above configuration is jsut a starting point.
I agree with following the "Specification" pattern. It will make everything much easier.
Here are the options that I came across while researching about this:
form
package, it should be more than enough to create such forms.The only catch however is that how the generated form data is expected on the front-end. I think we should may be create some mocks to easily draw a map of all the things that need to work together.
P.S. I also tried to explore many of the WP forms solutions out there but almost all of those codebases are private.
Conclusion: We already have everything we need to generate such forms:
form
package can handle the state of the form generators.form
package.react-beautiful-dnd
for Drag-n-Drop for sorting of entities, we can use the same in form generators.ok great, thnx for researching that.
I'm a little bit dismayed you didn't have any objections to the following:
the action menu contains options such as "edit", "copy", "delete", and "reorder".
edit will open a modal window with inputs for controlling all aspects of that element and will be the meat and potatoes of the application (more on this below)
copy would create a duplicate immediately after the copied element in the form
delete should obviously drop all tables and databases on the server, as well as delete all files including those belonging to the operating system. If possible, all data on all servers sharing the same host field of the current server's IP address should also be wiped clean. You can never take data privacy too serious. A confirmation modal should be triggered prior to continuing with deletion.
Well, there is a reason I mentioned creating mocks. Things will become clearer once we have a visual idea about the UI elements.
the general development roadmap might look like this:
here's a rough mockup to give you an idea of what we will be building in the admin
the final product will undoubtedly end up being a bit different as I'm sure there will need to be changes as we discover various changes in requirements
let's name this puppy the EDTR Reg Form Builder
So far, so good.
Background
Our current registration form schema utililzes a system carried over from EE3 consisting of questions and question groups of which the latter are associated with events.
This system has many shortcomings that we have encountered over the years including:
inability to add the same question to more than one question group, for example the "email" question is part of the "personal" question group and can not be added to any other groups due to the way that the model objets are related.
inability to associate conditional logic with a question for a particular event because questions are essentially global items shared with all events
inability to easily customize the registration form for different events, not just with the way question groups are created and assigned, but also with presentation.
inability to associate questions (or question groups) with individual datetimes or tickets
Task
As part of our modernization process involving the conversion of our presentation layer to React + Apollo & GraphQL, as well as facilitating the ability to provide our users with some long awaited features, we will be refactoring the registration form system. This will involve:
modifying the existing question related models in order to expand the customization and relational capabilities for forms
moving the registration form generation UI into the EDTR
saving unique records for each event as opposed to "global" records shared by all events
allowing questions or question groups to be tagged as "defaults" and automagically applied to all new events
allowing questions to be associated with specific datetimes or tickets
adding new models for enabling the use of conditional logic in registration forms
Data Schema
The following tables show the proposed GQL data schema with the corresponding server side model schema where new fields are marked with an
*
.Form Input | Question
Questions are the "Form Inputs", and we may want to refer to them as such from now on.
Form Section | Question Group
Question Groups are essentially "Form Sections", and we may want to refer to them as such from now on.
Form Relation | Event Question Group
This is currently the intersection table between Events and Question Groups, but we should refer to it from now on as "Form Relations" because this table will be getting heavily modified in order to expand what entities a question or question group can be related to
purchaser
,primary
,additional
,all
, ???EVT_ID
,DTT_ID
,TKT_ID
example data
Action (new)
A new table added to facilitate conditional logic. Actions specify what behaviour should occur when the associated conditional rules are satisfied. example: for the following Action Rule:
DISPLAY Form Input 456 IF Form Input 123 Answer === "potato"
the "action" might be "DisplayFormQuestion", the target would be "456", and the IF conditions would be handled by rules.example data
Action Object (new)
A new table added to facilitate conditional logic. It is the intersection table for connecting actions to other entities
EVT_ID
,DTT_ID
,TKT_ID
,QST_ID
example data
Action Rule (new)
A new table added to facilitate conditional logic. It is the intersection table for connecting actions to rules
(A or B) && (C or D)
has two groupsexample data : RULE 123 && RULE 124 && RULE 125
example data : RULE 123 || RULE 1324|| RULE 125
example data : RULE 123 && ( RULE 124 || RULE 125 )
example data : ( RULE 123 || RULE 124 ) && ( RULE 125 || RULE 126 )
If the above seems odd to you, it's because of the way the PHP model system operates. It may make more sense if you replace the following words with these phrases:
AND
"If ALL of the following are true" :A & B & C
OR
"If ANY of the following are true" :A or B or C
Rows with no ruleId mean that the following row is a subgroup, which is the only way I could think of to handle a set of rules like:
"If ALL of the following are true" : ( A or B ) && ( C or D )
which is depicted in the last table of example data above.Action Trigger (future use?)
A new table for dynamically controlling WHEN an action occurs, via crons, dates, or hooks.
cron
,date
,hook
Action Trigger Values
cron:
daily
|weekly
|monthly
date: ISO 8601 timestamp hook: name of hook, ex:AHEE__EE_Event__set_status__after_update
example data
hook
Rule (new)
This is a new table added to facilitate conditional logic. This model will be used for multiple features beyond conditional logic in the registration form, so some of its functionality might not make complete sense for the time being.
query
,model
,strategy
Comparison Enum Options
example data
query
BEFORE
query
=
model
PARAM
query
=
query
=
model
>=
Each record could be viewed as representing a single condition from the WHERE clause of an SQL query. example:
would utilize the following Rule fields
This is the initial issue for planning things out and is still a work in progress. Additional focused issues will be created for directing PRs.