The Plugin Walkthrough: Creating Your Own Tables
Movable Type provides the versatile mt_plugindata table, used by plugins to store all types of data. However as your plugin begins to grow in size, continuing to store data in mt_plugindata could start to become unwieldily and so today we'll explore how to create and work with custom tables. Custom tables have several advantages over mt_plugindata including:
Much quicker data storage/retrieval - every time data is stored or retrieved from
mt_plugindatait needs to be serialized or unserialized.Being able to index columns - for example, if you wished to list all the objects you store for
blog_id1, withmt_plugindatayou would need to load all your objects and then skip the ones whoseblog_idisn't 1. With a custom table, theblog_idcolumn could be indexed and you would be able to load just those records with ablog_idof 1.With custom tables, joining separate objects together is a piece of cake. Movable Type already does this with entries and authors (separate objects that are joined on
mt_entry.entry_author_idandmt_author.author_id) or entries, categories and placements (mt_entry.entry_id,mt_category.category_id,mt_placement.entry_idandmt_placement.category_id).New with Movable Type 3.31, creating a custom table (and hence a custom class) allows you to make your objects taggable using
MT::Taggable(I'll cover this in another article, promise!).
First off, to create a table in the database, you will need to create a subclass of MT::Object. The documentation contains a good example of how to subclass MT::Object but misses a few key ingredients which makes our job as plugin developers infinitely easier.
column_defs
If you remember, Movable Type 3.2 shipped with a new upgrader. With Movable Type 3.31, the upgrader was enhanced such that plugins could hook into it. The first step, however, is to use the column_defs key instead of (or in addition to) the columns key. With column_defs, rather than simply listing your columns, you will need to define their properties including column type, length, attributes and null status. The quickest way to find out what you can use with column_defs is to look at existing MT::Object subclasses (the best are MT::Blog and MT::Entry as they have the largest number of columns), however here's a quick example:
column_defs => {
'id' => 'integer not null auto_increment',
'foo_id' => 'integer',
'name' => 'string(25)',
'status' => 'smallint',
},
defaults
Another new key introduced was defaults. As the name suggests, it allows you to specify the default value for columns and again the easiest way to understand how it works is to look at existing MT::Object subclasses. Here's a quick example highlighting defaults:
column_defs => {
'id' => 'integer not null auto_increment',
'foo_id' => 'integer',
'name' => 'string(25)',
'status' => 'smallint',
},
default => {
'status' => '3'
}
Automating the Installation
With Movable Type 3.3 plugins can now have MT automatically create and maintain their tables. To do so, when registering the plugin, simply specify two new arguments, schema_version and object_classes. The first is similar to version but is specific to your database scheme (i.e. you could add new features to your plugin but retain the same database stucture), the second is just a list of your MT::Object classes:
my $plugin = new MT::Plugin({
name => "Example Plugin",
version => 1.5,
author_name => "Conan the Barbarian",
schema_version => 1.2
object_classes => [ 'MT::Foo', 'MT::Bar' ]
});
MT->add_plugin($plugin);
When a plugin with a new schema_version is uploaded to MT_DIR/plugins/, Movable Type will trigger the upgrade procedure which will install (or update) your plugin's tables.
Upgrade Functions
New with Movable Type 3.3, upgrade functions. These are functions that are fired when a plugin is installed or upgraded and is triggered by a change in schema_version. You can register these functions using the aptly named upgrade_functions key whilst registering your plugin:
my $plugin = new MT::Plugin({
name => "Example Plugin",
version => 1.5,
author_name => "Conan the Barbarian",
schema_version => 1.2,
object_classes => [ 'MT::Foo', 'MT::Bar' ],
upgrade_functions => {
'my_plugin_fix_field_a' => {
version_limit => 1.1, # runs for schema_version < 1.1
code => \&plugin_field_a_fixer
}
}
});
MT->add_plugin($plugin);
sub plugin_field_a_fixer {
# do stuff
}
However, at the time of writing, a bug in Movable Type 3.3 prevents upgrade functions from running if your plugin hasn't previously defined a schema_version. To fix this, add the following code to your plugin's .pl file (thanks to Tim Appnel and Brad Choate)
sub init {
my $plugin = shift;
$plugin->SUPER::init(@_);
MT->config->PluginSchemaVersion({})
unless MT->config->PluginSchemaVersion;
}
If you're like me, however, your code may not be perfect on the first go and you'll want to run the upgrade functions until they do work perfectly. The easiest way I've found to re-trigger the upgrade functions is to trick MT into thinking that the plugin's tables haven't yet been installed. This involves editing the database and removing the PluginSchemaVersion line for your plugin:
- Open the
mt_configtable. You should only have one record in the table - Edit the
config_datacolumn for this record - you'll notice that there are various configuration items here, one per line - Remove the
PluginSchemaVersionline that relates to your plugin, for example withMT Blogroll, I'd have to remove the line that readsPluginSchemaVersion Blogroll/Blogroll.pl=2.2 - Save
config_dataand go back to Movable Type, you should be prompted to reinstall your plugin's tables (and hence the upgrade functions will fire).
This takes care of installing and maintaining your tables. To learn more about how to use your new tables and classes, read the new online developer documentation for MT::Object.

Leave a comment