In this help we describe other methods that a model can implement over and above the required generate_code method. All of these methods are optional however you should consider implementing methods that improve your users' productivity when working with your model.
Sometimes errors may exist in the information developers enter into a specification. These errors should be identified in the spec_validation method. By validating here, instead of the generate_code method, the validations will be triggered if the user enters the check command. In addition to setting messages that describe the errors that have been detected, this method should also increment the variable $this->validation_error_count for each error encountered. The method should return false if the errors need to be addressed before proceeding to stow the specification. Return true if the specification passed all validation.
Here is an example of this method.
This validation is used by the image_sizes model. This model uses a two column flexgrid wherein column 1 is used to enter media queries and column 2 is used to enter width definitions. This code checks to make sure that the last row only contains a width value (and no media query) and that all other rows contain both a media query and a width.
In most cases you will want to use the messages model to define the error messages used in your validations. Generally, the messages specification name should be the same as the model name being validated unless you want to share common messages across multiple models.
This method returns false if errors were detected and true otherwise.
When assigning messages it is also possible to associate the message with a particular field by following the assign_message method with the assign_mesage_field method as shown here.
The second parameter to this method is the name of the field to be associated with the message. The first parameter can be used to qualify the field. When a field name is referenced the main message area does not show the specific message. Instead, the main messages refer to detailed messages within the specification area of the page as shown here:
The user must scroll the page to see the actual message(s):
Models can support a find/replace feature by implementing a change method. This will be called when the user enters a command such as change old new. The old and new values will be passed as parameters to the change method as shown here.
In this example we make use of a base method called generic_flexgrid_change that will apply the changes to all entries of the supplied flexgrid. If your model uses several flexgrids, you will call the generic_flexgrid_change for each grid that could be changed by the change command.
This next example shows how a change method is used to update two textareas
Notice that we pass a third parameter to the php str_replace function so that it informs us as to the number of replacements. We use the total number of replacements to set a suitable message. If replacements were performed, we also need to call the inherited method set_spec_changed_msg in order to remind the user that the specification has been changed and should be stowed.
Page oriented models which define an after_load method should also call the after_load method of the inherited page_spec class as in:
If present, a model's before_delete method is called just before a specification is deleted in response to the user's delete command. This method can prevent the deletion of a specification by returning false. Return true if the delete operation can proceed. Here is an example of before_delete used by the user model.
If needed, the after_stow method can be used to perform some processing after a specification has been successfully stowed. This method should return true unless there is a problem, otherwise the user will be alerted with an error such as the following:
Here we show how this method is used to clear the cache containing the list of valid dollar functions after stowing the current dollar function.
GenHelm supports a test feature whereby users can enter test in the command line to show a test mode section which renders the output from the current specification. Here we see an example of the Test Mode panel for a certain faq specification.
In order for a model to support this test mode feature it needs to implement a method named get_test_mode. Most of the page oriented models implement this method automatically by virtue of inheriting from page_spec. Here we see the code that is implemented by this base class:
This method must return an array with the following four keys defined:
Note that the test option shows the results of the specification at the time it was last stowed so users must restow the specification to see any recent changes.
Sometimes models can anticipate ways that specifications can be derived from information that is available from various components. In such cases they can implement an import command that users can issue to improve their productivity. As an example, reports are often created to show data that has been posted on a form, along with standard information such as date, time, IP address, etc. The report model makes it easy to handle this common case by allowing the user to import a form into the report specification. Here we show import code used by the model_report class.
By default, the import command assumes one parameter. If your model needs more parameters you must also supply a second method named import_parameter_requirements to indicate the minimum and maximum number of parameters supported. Here we see an example of this used by the report model:
Technically, the report model only needs one parameter, which is the name of the form being implemented. Nevertheless, this has been implemented to require two parameters so that the user must enter the import command as "import form formname". This notation has been chosen in case the report model introduces new import options in the future.
Models can optionally support a sort command by implementing a sort method. This function takes one parameter which indicates the field by which to sort. The sort implementation will vary based on the data used by the model. Here is an example of a sort method used by the glossary model. This uses a base class to sort a flexgrid by a supplied field name.
This method illustrates a GenHelm feature wherein methods can be supplemented or replaced by custom code. The GenHelm model model automatically generates a method named clear_spec_details. This method is called if the user explicitly uses the clear command and also when the model is loaded for the first time. By default, this model initializes all of the properties of the model to their default "empty" value (this is either a supplied initial value or zero for numeric fields and an empty string for character fields). Usually, the default handling is sufficient and no custom code is necessary. Occasionally you may develop a model wherein you want to replace or supplement this default method code. Here we show an example where the default code has been replaced with custom code:
By changing the Location option you could also choose to supplement the default code by adding your own code either before or after the standard clear code.
The other interesting thing to note about this code is that it uses the inherited method scalar_field_definitions. This returns the collection of fields used by the current model. Using this lets us clear all the fields without naming each field individually. This makes the code more nimble since this code won't need to be updated if new fields are added.
You may be wondering "how does one incorporate required or "supplementable" functions into the custom code of a model?". For help on this topic please click supporting custom code.
So far, many of the optional methods we have described are used to support commands that users can enter on the GenHelm command line. These include:
Models only need to implement these commands when it makes sense to do so. For example, if "sort" does not make sense within the context of a certain model, the optional sort method should not be implemented.
It is also possible for models to implement custom commands. These can either be implemented as command line commands or as buttons. Let's review these options next.
Custom Command Line Commands
To implement a new command that users can enter into the GenHem command line you must code a supporting method in your model named custom_command_name, where name is the custom command to be implemented. This method can accept up to two parameters:
For example, if the user enters the command "a b c d" while editing the specification named spec1, GenHelm would check to see whether the current model implements a method named custom_command_a. If it implements this method it will be called passing 'spec1' as parameter 1 and array('b','c','d') as parameter 2.
Let's look at a real example of a custom command implemented by the image model. This model supports a custom command named scratch. This command is used to delete an image definition along with all of the images referenced by the definition. The normal delete command, supported by all models, deletes the specification as well as any components generated by the model but it does not delete "related" components. Therefore, using the delete command for image definitions will delete the image specification and the generated .inc definition but it won't delete the actual image files referenced by the definition. The custom scratch command can be used for this purpose. Here we show the code for the scratch command.
Since the scratch command only applies when the user is editing a saved specification we begin by making sure we have a specification name in parameter 1. We also make sure the user has not passed any parameters to the scratch command, since it does not support any parameters.
The code then goes through all of the linked image files and deletes these if they exist. In addition to deleting the related images, this code also calls the self_destruct method of the base class to inherit the normal delete behavior.
Custom Button Commands
There are two approaches by which models can support custom buttons:
The second option should be used if the command can be handled independently of the model's form. For example, it is possible to instantiate and call model objects programmatically outside of the context of the model's main form. If the command you are implementing might be needed in this context it is best to implement the command handler method directly within the model class.
Implementing Buttons Within a Form Handler
The process of handling buttons from within a form_handler class is similar to the way buttons are handled by any form. That is:
As an example we will temporarily add a form handler and button to the anchor model form e_anchor as shown here:
The button handler is implemented as a function within the form_handler class. Here is a trivial example of such a function:
Obviously this button handler does not do anything except write a value to the form's debug area. Since this method returns false, processing will not continue to call the model_anchor class. If this function returns true, it would be necessary to have a method named some_button in the anchor model class to handle this command.
Here we can see the anchor form after pressing "some_button":
Notice that, by default, the label for the button also needs to be added to the anchor translation object. Alternatively, you could add a property *label:Some Label to the button.
Implementing Button Commands Within the Model Class
The second approach to implementing button commands is to add a method to the model class whose name matches the button. With this option there is no need to implement a custom form_handler. The method in the model class will be passed the name of the current specification in parameter 1. Here we see an example of how some_button could be implemented within the anchor model class:
Pressing this button would simply write the current specification parameters to the debug area as shown here:
---------------- DEBUG ----------------
Finishing Off Your Model
So far we have described all of the key components needed to implement a model, in the final section we will describe how you can add help for the model.