Writing customizable and upgradable applications

I was working on a WordPress plugin and I wanted to make it both easily customized by the user as well as allowing standard WordPress upgrades to happen while not destroying the customizations. When WordPress does an upgrade, it removes the plugin or theme and installs the upgraded item. If the user had customized it in the usual manner (note – I did not say correct) the customizations will be lost. I came up with the following piece of code:

 

/**
* Function add_class
*/
function add_gd_class($classname) {
  include_once (str_replace('\\', '/', plugin_dir_path( __FILE__ )).'includes/'.$classname.'.php');
  $custom_dir = str_replace('\\', '/', plugin_dir_path( __FILE__ ));
  $custom = substr($custom_dir, 0, strlen($custom_dir) - 1).'_custom/'.$classname.'_custom.php';
  if (file_exists($custom)) {
    include_once (str_replace('\\', '/', plugin_dir_path( __FILE__ )).'custom/'.$classname.'_custom.php');
    $custom = $classname.'_custom';
    $class = new $custom();
  } else {
   $class = new $classname();
  }
  $class->initialize(plugin_dir_url( __FILE__ ), str_replace('\\', '/', plugin_dir_path( __FILE__ )), 'dmc_user_list');
}

So during plugin initialization, I use the following to load the actual plugin:

add_gd_class('gd_core');
  if (is_admin()) {
    add_gd_class('gd_admin');
  } else {
    add_gd_class('gd_user');
  }

And the plugin is loaded, constants are defined in the classes and they initialize themselves. A user can modify the classes by writing a child class and placing it in a folder in the plugins folder and put modifications in there. As long as the naming conventions are followed, all works well the modifications are isolated from the plugin and we have achieved our goal.

Now onto the more general discussion. Note, that I am using php in my examples but any object oriented language such a C++ or Java can be used.

Why  do users want to alter an application. Simply, it does not fit their requirements. More than 90% of the time this is the case. Some users will live with it but many want a better fit. What do they want changed? They may need to save additional data, they may want to change the user interface or they may want to change the business logic.

Changing the database schema

The main concern here is that the user has additional data to store.In the logical sense, this means adding fields and tables.

The first requirement is that there is some means to add these fields. This could be to simply go into the database management system and adding the objects. Unfortunately this does not inform the app the changes have been made and this will necessitate making change in the User Interface and adding app logic to manage the data.

An alternative is to provide additional ‘User’ fields in the database. With the ability to define the meaning of these fields it will work but you can only add a limited vendor defined number of fields and you can’t add additional tables. Why might you want to add a table? The example I use is that you provide employees in a field that requires certifications and licenses. If you have a field for license and one for certification, you can only mark one of each but what is to prevent you clients from having more than one.

The third option is to provide a text file that describes the schema and a tool that will update the database to match the schema. You can allow the user to modify this file but preferable is to have the tool look for a custom schema that describes the changes to the database. When the app is upgraded, the new schema is provided and merges with the custom file keeping the changes intact. I have created such a tool and I am working on another version that allows the definition of metadata to allow the app to maintain and display the custom data without any changes to the app.

One could also provide a schema editor that would allow changes to the schema to be made and it will deal with changing the database.

The next consideration is how will you allow the users t9o modify the database. I have seen three basic methods. The first is to provide ‘meta’ tables. This is the methodology that WordPress uses. Both the Users and the Posts tables have an associated meta table. In the app, columns are defined as additions to one of these tables and the data is stored as a key and value pair associated with the parent row in the appropriate parent table. It is a small matter of creating a custom field. WP also allows the creation of Custom Post Types that would represent a new table. The issue with this scheme is that multiple database pages would have to be read to retrieve all the associated rows and having many logical tables in one physical table could cause issues. The fortunate thing about WP is that a developer can create separate tables to hold their data.

The second method I have seen is the use of a parallel ‘custom’ table as evidenced in SugarCRM. Sugar places new fields in the custom table and they are related one for one with the parent table. No real problem with this unless you are a database purist as one to one relationships are eliminated in the normalization process (see Database normalization). Sugar does allow the creation of new tables but if the user modifies it after creation, the custom table will be used.

The method that the database prefers is to allow base tables to be extended by adding fields to the tables as well as the ability to add new tables.

Changing the user interface

This includes both the general look and feel of the application. For the first, we are talking about  a theming system. If you are writing a plugin for a CMS, this is taken care of by the CMS. If you are writing a standalone app, this becomes a concern and you need to separate the page structure from the content that you are displaying. Break the display into sections and write modules that handle each portion such as HTML header, page header, content area, sidebar and footer. It is then advised that you provide a file structure that allows multiple themes being installed in the app and some management page that allows the selection of a theme.

A note here, I like WordPress child themes that allow the modification of a theme without touching the original theme. This allows the original theme to be upgraded without losing any of the modifications. Very powerful.

Changing the program logic

This section involves both changing the algorithms the application uses as well as displaying new fields that have been added to the database.

The first method would be to provide hooks that allow the insertion user created code into the processing of the application. Consider:

if ( !function_exists('hook_name') ) $value =hook_name($input);

This code would allow an externally defined function to be called if it exists. It could also be used to override an existing function:

 

if ( !function_exists('function_name') ) :
  function function_name($inputs) {
    ...
    return $return;
  }
endif;

This method is used by WordPress for ‘pluggable’ functions. The programmer of the application just has to insure the custom code is loaded before the pluggable functions are executed.

Another method would be to allow the user to create parts of a parallel code structure where modified copies of code can be placed. The module loader would check for the modified module and load it instead of the original module. SugarCRM uses this method. The issue with his method is that the modules must be kept small so that the probability of changes not being applied to executing code and the user must take care to insure that any updates are applied to the custom code manually. This also requires that the programmer completely document where changes have been made.

The final method id to use object oriented programming and inheritance. The function add_class above implements this scheme. The custom module would have to follow certain naming restrictions. Because I developed this principle when writing a WordPress plugin where the plugin folder is deleted and the new plugin is installed, I placed a second directory in the plugins folder. The custom code would be as follows:

class module_custom extends module {
	
  function __construct() {
    parent::__construct();
  }

  public function custom_function( $inputs ) {
    ...
    return $return;
  }
}

I have also done the similar with CodeIgniter but you have to write a loader helper that looks for the modified module and loads it and the original. If you want the module auto-loaded, a skeleton custom module needs to be provided and the updates must not include these custom modules.

Conclusion

These are meant only as guidelines to inspire programmers to evaluate their application design and to insure that users can create customizations to the application. I have yet to see an applications to fits customer requirements more than 90% so either the customer must modify their process to fit the app or be able to modify the app.

Comments are closed.