Recall that our "mission" is to create a model that will generate code similar to the following:

$this->set_div_wrapper_properties('class="override"');
$this->set_add_question_numbers(true);
$this->div_start();
$this->add_heading('Section Heading 1');
$this->add_question('Question 1',
		array('Answer 1 Paragraph 1', 'Answer 1 Paragraph 2', 'Answer 1 Paragraph 3'),
		true);
$this->add_question('Question 2','Answer 2 Single Paragraph');
$this->add_heading('Section Heading 2');
$this->add_question('First question of Section 2',
		array('Answer to first question in section 2.','Another paragraph.'));
$this->div_end();

In this help we will describe how to implement the main components that make up the faq model.

Creating the faq Model

Let's jump ahead a little bit and show what our faq model input form will look like:

faq Model Input Form

This form is what you would expect given the properties and methods of our runtime faq class (html_page_faq). Let's review the fields on this form:

  1. We have a checkbox to allow the user to select whether to include question numbers. This will be used to call the set_add_question_numbers method.
  2. There is a textarea to enter the optional div properties. If a value is supplied in this field we will use this to call the set_wrapper_div_properties method.
  3. Next we have a field that allows the user to automatically open some of the questions. We are going to make this a select field so that the user can choose to either open just the first question or all of the questions.
  4. Next we use a flexgrid to allow the user to enter any number of questions and answers as well as specify an optional section heading for which we will generate h2 tags in front of certain groups of questions. Since each question can have answers consisting of more than one paragraph we will allow the user to add columns to the flexgrid for paragraph 2, 3, etc.
  5. Finally, since the faq  model can be used to generate entire pages, we need all of the other properties associated with all page-oriented models. We have just shown the Common Page Properties here but all of the other properties such as Meta Tags, Script, CSS, etc. will follow.

Before we create our faq model object, let's create the select field and flexgrid since we will need to refer to these in our model.

Create Special Fields Used by the Model

While logged into the genhelm site edit the select model to build our auto open selector. Here we see the three options from which the user can select:

Auto Open Selection Field

We stow this as auto_open_faq.

Next we will use the flexgrid model to create the grid that is used to enter the section headings, questions and answers. First let's look at the scalar properties that we need:

Flexgrid Scalar Properties

Let's review the fields that we have set:

  1. The Flexgrid Description will be used in certain messages that refer to the grid.
  2. We set the Table Height to auto so that it will grow based on the number of questions entered into the grid.
  3. The grid must have at least three columns to accommodate the Section Heading, Question and Answer Paragraph 1 so we have made the first 3 columns "fixed". We have also set the initial number of columns to 3.
  4. Since we want to allow the user to insert more columns to handle long answers that span several paragraphs we mark the option to allow columns to be inserted.
  5. We will have the grid start with five rows so we set the Initial Rows to 5.
  6. Finally, we want to allow more rows to be added to accommodate any number of questions so we check the Allow Insert Rows option.

Next we need to configure the columns of the flexgrid as shown below (note that some empty columns right of what is shown have been excluded):

FAQ Model Flexgrid Definition

The rows define each of the columns in the flexgrid. Most of the settings are self-explanatory. The fourth row uses a reserved column name of new(). This row will be used to configure new columns that are added by the users of the flexgrid to insert more answer paragraphs. Without this new() definitions, new columns would be inserted without a header. Refer to the flexgrid model help for more information about configuring flexgrids.

Create the Model Class

Now we will turn our attention to the faq model class. All models are named using the convention model_[name] so this model class is named model_faq. All models are created within the genhelm site. Here we see the scalar parameters for this model.

faq Model Scalar Parameters

Let's go over all of these settings.

Base Class

Since the faq model will be used to generate an entire web page, we set our Base Class to page_spec. This allows us to inherit all of the page-oriented properties and methods such as those dealing with meta tags, CSS, script, layouts, etc. For models that are not intended to generate pages, the base class is normally left empty. In such a case the model will inherit from the spec class by default. On rare occasions you might create a model that inherits another model. This is done when the functionality of one model closely aligns with another.  

Default Spec Name

In cases where a website would typically only have one component of a certain type (generated by the model) you can indicate a default spec name within the model. When a default is specified, users can use the stow command without specifying a specification name. If a specification does not already exist with the default name it will be used as the name for the newly stowed specification. If there is a default spec name indicated, this specification will also be loaded by default whenever the model is used.

Spec Subfolder

If specifications for this model are to be stored within a certain subfolder of the spec folder, enter the name of the subfolder here.

Special Form Name

By default, the form used to enter the specification for all models is e_[model_name]. If your model does not use this default naming convention you can enter an alternate name here.

Required Access Level

Developers can be assigned access rights using the logon_access model. This field can be used to restrict use of the current model to only those developers who have sufficient access.

Default Translation

All supplied models use classes generated using the translation model to provide values for field labels and associated help.  As we will see, the name of the translation class will be specified as part of the form associated with the model. Occasionally, you may want to use an alternate translation class to translate flexgrid headings used by the model. If so, enter the name of the class here.

Model Description

Use this field to describe the purpose or usage of the model. This description is shown when developers use the new command to obtain a list of models from which to choose.

Generates

Provide information about what the model generates. This will also be shown by the new command so try to keep this relatively short. Typical examples include php include, php class, css, xml, etc.

Supports Dollar Functions

If the current model is used in conjunction with certain dollar functions, list these here. This will also be shown by the new command.

Default Spec Description

Whenever any model is used to generate certain components, it is necessary to enter a description to be associated with the specification. You can use this field to provide a default description that the user (developer) can change to something more specific.

Site Specific

If the current model can only be used in conjunction with a certain site, enter the site directory here. For example, all dollar_functions are generated within the system site so that the resultant classes (dollar functions) can be used by any site.

Runtime Page Class

If this model is supported by a certain class at runtime (when the content produced by the model is rendered), you can enter the name of the class here. This is used for documentation purposes.

Standard Model Image

When users use a model, it is possible to show an image in the model summary pane. If this option is selected, you must define an image definition with the same name as the model within the genhelm image rendition folder.  Here we see an example of the faq rendition image definition (rendition/faq). 

faq Rendition

Don't make these rendition images very large since they need to fit in the component summary area.

Spec Specific Image

Sometime it is more suitable to show a rendition that is specific to the specification being edited. For example, when editing image definitions it is helpful to see the actual image that is being defined. When this option is selected, the model class must define a method named get_spec_image. This method must return a url reference to the image to be shown in the rendition window.

Obfuscate

Check this option to obfuscate the code for your model. Of the supplied model, only the model_model is obfuscated to prevent users from breaking this model.

Defining the Fields Used by the Model

All of the fields used as part of the model specification must be defined in the model definition. These are broken down into Scalar Fields and Flexgrid fields.

Here we see the Scalar Fields used by the faq model.

faq Model Scalar Field definition

All scalar fields must be given a name, XML Type and UI Control. The XML Type determines how the XML used to save the specification will be structured. Generally speaking, simple properties can be defined as attributes while more complex or lengthy properties should be stored as elements. When in doubt use the Element option.

Choose the UI control that is most appropriate for the field value to be entered. In this example, since we are using a select field to enter the auto_open value, we must identify the name of the select class within the UI Parms.

We don't need to provide Field Labels or Translation classes since we will be using the defaults. By default, the labels are defined with popup help to give users of the model hints as to the purpose of the fields. This feature can be suppressed for certain labels by marking the Suppress Infobox option. You can provide an initial default value for certain fields if you wish using the next column. Certain values can also be indicated as being required.

The Validations column can be used to define built in field validation. Validations must be defined as classes within genhelm/classes/field_validator and must be named validate_[name].  The validation class must contain a method named is_valid. Here is an example of such a class that is used to validate whether a supplied value coincides with the name of a class in the system classes folder:

require_once CLASS_PATH.'field_validator_base.php';
class validate_framework_class extends field_validator_base
{
//custom function + {
function isvalid($field_label,$field_value,$field_structure,$field_name,$field_occurrence=false) {
	if (!file_exists($this->site->get_framework_path('classes').'page_component/'.$field_value.'.php')) {
		$this->assign_message('model',2002,array('Runtime',$field_value),':1: class :2: is not defined in the framework');
		$this->assign_message_field($field_structure,$field_name,$field_occurrence);
		return false;
		}
		return true;
}
//custom }
}

Next we add the flexgrid definition that was created to enter the questions and answers::

faq Model Flexgrids

Developing the Model Code

At the very least, the model must contain one function named generate_code. This function must return an array for each component to be saved. The actual saving of components is handled by the spec base class, as is the generation of the XML used to capture the specification.

Each array occurrence used to describe components to be saved must contain the following keys:

folder

This is name of the folder in which the component will be saved. This is not a full folder path but rather just the first node under the site's public_html or private_data folder.

name

This is the file name under which the generated component will be saved.

contents

This is the (generated) contents that will be saved. When the name refers to a zip file, the contents defines the list of files to be included in the zip.

subfolder

The subfolder key is optional. If supplied, this will be used to define the subfolder in which to save the generated content.

Before working out all of the details of what your model will be generating, you might want to create a simple function such as the one we show below:

Model boiler plate code

This will just generate some static code but it will give you a chance to build a form for your model without getting bogged down with what the model will be generating.

In the example above, the code will only generate one component since only one occurrence of an array is returned. 

Notice that we don't include the function statement or the opening and closing function bracket. This is because we have entered the function name in the Section Id and used Replace Body as the Location indicator.

The $spec_name field is a parameter that is passed to the generate_code function. This is the name that the user included in the stow command. For example, if the user issued the command stow fred/flintstone, the value of $spec_name would be 'fred/flintstone'.

We will come back to the model custom code later but first we will define a form for the model so that we can properly test the model code as we develop it.

Model Input Form

Recall that, by default, the name of your model form is e_[model_name]. Next we will go over the contents of our e_faq form. Here we see the main form parameters:

faq Model Form - Part 1

We have indicated that the translation class will be faq. This is where the form's labels will be saved.

By using ? as the default label, this means that, by default, all labels will be taken from the translation class and will include popup help.

In the Field Definitions field we have specified the class associated with our model. The form is able to call this object to obtain the field definitions because the spec base class defines the required field_definitions method. 

Next we see the form definition which defines the layout for the faq model form:

FAQ model form

The first three rows simply show scalar fields with :: used in column 1 as the placeholder for the translated labels.

On row 4 we have used the infolabel dollar function to introduce the flexgrid. Row 5 is the flexgrid itself.

On the last row we include the common_page_properties form which contains all of the other properties associated with all forms.

We define e_faq as a Page Component without URL access since this only runs within the context of the main GenHelm form.

That's all we need to do to create our model form.

Define Translation Class

The final step in building our model interface is to create the translation class named faq. This will provide all of the labels and popup help values to the form.

Translation Class to translate labels and help

At this point the model could be loaded and the model screen will be shown. Next we are going to dive deeper into how we complete the coding of the generate_code method.

Specification Used by the Model Model
🡇
Specification Used by the Model Model