Skip to content

devguide objectreference

Violet edited this page Nov 2, 2010 · 4 revisions

MT::Object Reference

The following is guide for developers on using MT::Object - the basis for how objects, data structures and persistence are managed in Melody.

NAME

MT::Object - Melody base class for database-backed objects

SYNOPSIS

Creating an MT::Object subclass:

package MT::Foo;
use strict;

use base 'MT::Object';

__PACKAGE__->install_properties({
    columns_defs => {
        'id'  => 'integer not null auto_increment',
        'foo' => 'string(255)',
    },
    indexes => {
        foo => 1,
    },
    primary_key => 'id',
    datasource => 'foo',
});

Using an MT::Object subclass:

use MT;
use MT::Foo;

## Create an MT object to load the system configuration and
## initialize an object driver.
my $mt = MT->new;

## Create an MT::Foo object, fill it with data, and save it;
## the object is saved using the object driver initialized above.
my $foo = MT::Foo->new;
$foo->foo('bar');
$foo->save
    or die $foo->errstr;

DESCRIPTION

MT::Object is the base class for all Melody objects that will be serialized/stored to some location for later retrieval.

Melody objects know nothing about how they are stored - they know only of what types of data they consist, the names of those types of data (their columns), etc. The actual storage mechanism is in the Data::ObjectDriver class and its driver subclasses; MT::Object subclasses, on the other hand, are essentially just standard in-memory Perl objects, but with a little extra self-knowledge.

This distinction between storage and in-memory representation allows objects to be serialized to disk in many different ways. Adding a new storage method is as simple as writing an object driver - a non-trivial task, to be sure, but one that will not require touching any other Melody code.

CREATING A SUBCLASS

Creating a subclass of MT::Object is very simple; you simply need to define the properties and metadata about the object you are creating. Start by declaring your class, and inheriting from MT::Object:

package MT::Foo;
use strict;

use base 'MT::Object';

__PACKAGE__->install_properties($args)

Then call the install_properties method on your class name; an easy way to get your class name is to use the special __PACKAGE__ variable:

__PACKAGE__->install_properties({
    column_defs => {
        'id' => 'integer not null auto_increment',
        'foo' => 'string(255)',
    },
    indexes => {
        foo => 1,
    },
    primary_key => 'id',
    datasource => 'foo',
});

install_properties performs the necessary magic to install the metadata about your new class in the MT system. The method takes one argument, a hash reference containing the metadata about your class. That hash reference can have the following keys:

column_defs

The definition of the columns (fields) in your object. Column names are also used for method names for your object, so your column name should not contain any strange characters. (It could also be used as part of the name of the column in a relational database table, so that is another reason to keep column names somewhat sane.)

The value for the columns key should be a reference to an hashref containing the key/value pairs that are names of your columns matched with their schema definition.

The type declaration of a column is pseudo-SQL. The data types loosely match SQL types, but are vendor-neutral, and each MT::ObjectDriver will map these to appropriate types for the database it services. The format of a column type is as follows:

'column_name' => 'type(size) options'

The 'type' part of the declaration can be any one of:

  • string - For storing string data, typically up to 255 characters, but assigned a length identified by '(size)'.

  • integer - For storing integers, maybe limited to 32 bits.

  • boolean - For storing boolean values (numeric values of 1 or 0).

  • smallint - For storing small integers, typically limited to 16 bits.

  • datetime - For storing a full date and time value.

  • timestamp - For storing a date and time that automatically updates upon save.

  • blob - For storing binary data.

  • text - For storing text data.

  • float - For storing floating point values.

Note: The physical data storage capacity of these types will vary depending on the driver's implementation.

The '(size)' element of the declaration is only valid for the 'string' type.

The 'options' element of the declaration is not required, but is used to specify additional attributes of the column. Such as:

  • not null - Specify this option when you wish to constrain the column so that it must contain a defined value. This is only enforced by the database itself, not by the MT::ObjectDriver.

  • auto_increment - Specify for integer columns (typically the primary key) to automatically assign a value.

  • primary key - Specify for identifying the column as the primary key (only valid for a single column).

  • indexed - Identifies that this column should also be individually indexed.

  • meta - Declares the column as a meta column, which means it is stored in a separate table that is used for storing metadata. See "Metadata" for more information.

  • indexes - Specifies the column indexes on your objects.

The value for the indexes key should be a reference to a hash containing column names as keys, and the value 1 for each key - each key represents a column that should be indexed:

indexes => {
    'column_1' => 1,
    'column_2' => 1,
},

For multi-column indexes, you must declare the individual columns as the value for the index key:

indexes => {
    'column_catkey' => {
        columns => [ 'column_1', 'column_2' ],
    },
},

For declaring a unique constraint, add a 'unique' element to this hash:

indexes => {
    'column_catkey' => {
        columns => [ 'column_1', 'column_2' ],
        unique => 1,
    },
},
  • audit

Automatically adds bookkeeping capabilities to your class - each object will take on four new columns: created_on, created_by, modified_on, and modified_by. The created_on, created_by columns will be populated automatically (if they have not already been assigned at the time of saving the object). Your application is responsible for updating the modified_on, modified_by columns as these may require explicit application-specific assignments (i.e., your application may only want them updated during explicit user interaction with the object, as opposed to cases where the object is being changed and saved for mechanical purposes like upgrading a table).

  • datasource

The name of the datasource for your class. The datasource is a name uniquely identifying your class - it is used by the object drivers to construct table names, file names, etc. So it should not be specific to any one driver.

Please note: the length of the datasource name should be conservative; some drivers place limits on the length of table and column names.

  • meta

Specify this property if you wish to support the storage of additional metadata for this class. By doing so, a second table will be declared to MT's registry, one that is designed to hold any metadata associated with your class.

  • class_type

If class_type is declared, an additional 'class' column is added to the object properties. This column is then used to differentiate between multiple object types that share the same physical table.

Note that if this is used, all searches will be constrained to match the class type of the package.

  • class_column

Defines the name of the class column (default is 'class') for storing classed objects (see 'class_type' above).

USAGE

System Initialization

Before using (loading, saving, removing) an MT::Object class and its objects, you must always initialize the Melody system. This is done with the following lines of code:

use MT;
my $mt = MT->new;

Constructing a new MT objects loads the system configuration from the mt.cfg configuration file, then initializes the object driver that will be used to manage serialized objects.

Creating a new object

To create a new object of an MT::Object class, use the new method:

my $foo = MT::Foo->new;

new takes no arguments, and simply initializes a new in-memory object. In fact, you need not ever save this object to disk; it can be used as a purely in* memory object.

Setting and retrieving column values

To set the column value of an object, use the name of the column as a method name, and pass in the value for the column:

$foo->foo('bar');

The return value of the above call will be bar, the value to which you have set the column.

To retrieve the existing value of a column, call the same method, but without an argument:

$foo->foo

This returns the value of the foo column from the $foo object.

$obj->init()

This method is used to initialize the object upon construction.

$obj->set_defaults()

This method is used by the init method to set the object defaults.

Saving an object

To save an object using the object driver, call the save method:

$foo->save();

On success, save will return some true value; on failure, it will return undef, and you can retrieve the error message by calling the errstr method on the object:

$foo->save
    or die "Saving foo failed: ", $foo->errstr;

If you are saving objects in a loop, take a look at the "Note on object locking".

Loading an existing object or objects

$obj->load()

$obj->load_iter()

You can load an object from the datastore using the load method. load is by far the most complicated method, because there are many different ways to load an object: by ID, by column value, by using a join with another type of object, etc.

In addition, you can load objects either into an array (load), or by using an iterator to step through the objects (load_iter).

load has the following general form:

my $object = MT::Foo->load( $id );

my @objects = MT::Foo->load(\%terms, \%arguments);

my @objects = MT::Foo->load(\@terms, \%arguments);

load_iter has the following general form:

my $iter = MT::Foo->load_iter(\%terms, \%arguments);

my $iter = MT::Foo->load_iter(\@terms, \%arguments);

Both methods share the same parameters; the only difference is the manner in which they return the matching objects.

If you call load in scalar context, only the first row of the array @objects will be returned; this works well when you know that your load call can only ever result in one object returned - for example, when you load an object by ID.

\%terms should be either:

  • The numeric ID of an object in the datastore.

  • A reference to a hash.

The hash should have keys matching column names and the values are the values for that column.

For example, to load an MT::Foo object where the foo column is equal to bar, you could do this:

my @foo = MT::Foo->load({ foo => 'bar' });

In addition to a simple scalar, the hash value can be a reference to an array; combined with the range setting in the \%arguments list, you can use this to perform range searches. If the value is a reference, the first element in the array specifies the low end of the range, and the second element the high end.

  • A reference to an array.

In this form, the arrayref contains a list of selection terms for more complex selections.

my @foo = MT::Foo->load( [ { foo => 'bar' }
    => -or => { foo => 'baz' } ] );

The separating operator keywords in between terms can be any of -or, -and, -or_not, -and_not (the leading '-' is not required, and the operator itself is case-insensitive).

Values assigned to terms for selecting data can be either simple or complex in nature. Simple scalar values require an exact match. For instance:

my @foo = MT::Foo->load( { foo => 'bar' });

This selects all MT::Foo objects where foo == 'bar'. But you can provide a hashref value to provide more options:

my @foo = MT::Foo->load( { foo => { like => 'bar%' } });

This selects all MT::Foo objects where foo starts with 'bar'. Other possibilities include not_like, not_null, not, between, >, >=, <, <=, !=. Note that not and between both accept an arrayref for their value; between expects a two element array, and not will accept an array of 1 or more values which translates to a NOT IN (...) SQL clause.

\%arguments should be a reference to a hash containing parameters for the search. The following parameters are allowed:

sort => "column"

Sort the resulting objects by the column column; column must be an indexed column (see "indexes", above).

Sort may also be specified as an arrayref of multiple columns to sort on. For example:

sort => [
    { column => "column_1", desc => "DESC" },
    { column => "column_2", }   # default direction is 'ascend'
]

direction => "ascend|descend"

To be used together with a scalar sort value; specifies the sort order (ascending or descending). The default is ascend.

limit => "N"

Rather than loading all of the matching objects (the default), load only N objects.

offset => "M"

To be used together with limit; rather than returning the first N matches (the default), return matches M through N + M.

start_val => "value"

To be used together with limit and sort; rather than returning the first N matches, return the first N matches where column (the sort column) is greater than value.

range

To be used together with an array reference as the value for a column in \%terms; specifies that the specific column should be searched for a range of values, rather than one specific value.

The value of range should be a hash reference, where the keys are column names, and the values are all 1; each key specifies a column that should be interpreted as a range.

MT::Foo->load( { created_on => [ '20011008000000', undef ] },
    { range => { created_on => 1 } } );

This selects MT::Foo objects whose created_on date is greater than 2001-10-08 00:00:00.

range_incl

Like the 'range' attribute, but defines an inclusive range.

join

Can be used to select a set of objects based on criteria, or sorted by criteria, from another set of objects. An example is selecting the N entries most recently commented-upon; the sorting is based on MT::Comment objects, but the objects returned are actually MT::Entry objects. Using join in this situation is faster than loading the most recent MT::Comment objects, then loading each of the MT::Entry objects individually.

Note that join is not a normal SQL join, in that the objects returned are always of only one type - in the above example, the objects returned are only MT::Entry objects, and cannot include columns from MT::Comment objects.

join has the following general syntax:

join => MT::Foo->join_on( JOIN_COLUMN, I<\%terms>, I<\%arguments> )

Use the actual MT::Object-descended package name and the join_on static method providing these parameters: JOIN_COLUMN is the column joining the two object tables, \%terms and \%arguments have the same meaning as they do in the outer load or load_iter argument lists: they are used to select the objects with which the join is performed.

For example, to select the last 10 most recently commmented-upon entries, you could use the following statement:

my @entries = MT::Entry->load(undef, {
    'join' => MT::Comment->join_on( 'entry_id',
                { blog_id => $blog_id },
                { 'sort' => 'created_on',
                  direction => 'descend',
                  unique => 1,
                  limit => 10 } )
});

In this statement, the unique setting ensures that the MT::Entry objects returned are unique; if this flag were not given, two copies of the same MT::Entry could be returned, if two comments were made on the same entry.

unique

Boolean flag that ensures that the objects being returned are unique.

This is really only useful when used within a join, because when loading data out of a single object datastore, the objects are always going to be unique.

window_size => "N"

An optional attribute used only with the load_iter method. This attribute is used when requesting a result set of large or unknown size. If the load_iter method is called without any limit attribute, this is set to a default value of 100 (meaning, load 100 objects per SELECT). The iterator will yield the specified number of objects and then issue an additional select operation, using the same terms and attributes, adjusting for a new offset for the next set of objects.

Removing an object

$foo->remove()

To remove an object from the datastore, call the remove method on an object that you have already loaded using load:

$foo->remove();

On success, remove will return some true value; on failure, it will return undef, and you can retrieve the error message by calling the errstr method on the object:

$foo->remove
    or die "Removing foo failed: ", $foo->errstr;

If you are removing objects in a loop, take a look at the "Note on object locking".

Removing select objects of a particular class

Combining the syntax of the load and remove methods, you can use the static version of the remove method to remove particular objects:

MT::Foo->remove({ bar => 'baz' });

The terms you specify to remove by should be indexed columns. This method will load the object and remove it, firing the callback operations associated with those operations.

Removing all of the objects of a particular class

To quickly remove all of the objects of a particular class, call the remove_all method on the class name in question:

MT::Foo->remove_all();

On success, remove_all will return some true value; on failure, it will return undef, and you can retrieve the error message by calling the errstr method on the class name:

MT::Foo->remove_all
    or die "Removing all foo objects failed: ", MT::Foo->errstr;

Removing all the children of an object

$obj->remove_children([ %param ])

If your class has registered 'child_classes' as part of its properties, then this method may be used to remove objects that are associated with the active object.

This method is typically used in an overridden 'remove' method.

sub remove {
    my $obj = shift;
    $obj->remove_children({ key => 'object_id' });
    $obj->SUPER::remove(@_);
}

The 'key' parameter specified here lets you identify the field name used by the children classes to relate back to the parent class. If unspecified, remove_children will assume the key to be the datasource name of the current class with an '_id' suffix.

$obj->remove_scores( %terms, %args );

For object classes that also have the MT::Scorable class in their @ISA list, this method will remove any related score objects they are associated with. This method is invoked automatically when Class->remove or $obj->remove is invoked.

Getting the count of a number of objects

To determine how many objects meeting a particular set of conditions exist, use the count method:

my $count = MT::Foo->count({ foo => 'bar' });

count takes the same arguments as load and load_iter.

Determining if an object exists in the datastore

To check an object for existence in the datastore, use the exists method:

$obj->exists()

if ($foo->exists) {
    print "Foo $foo already exists!";
}

To test for the existence of an unloaded object, use the 'exist' method:

Class->exist( %terms )

if (MT::Foo->exist( { foo => 'bar' })) {
    print "Already exists!";
}

This is typically faster than issuing a count call.

Counting groups of objects

Class->count_group_by()

The count_group_by method can be used to retrieve a list of all the distinct values that appear in a given column along with a count of how many objects carry that value. The routine can also be used with more than one column, in which case it retrieves the distinct pairs (or n-tuples) of values in those columns, along with the counts. Yet more powerful, any SQL expression can be used in place of the column names to count how many object produce any given result values when run through those expressions.

$iter = MT::Foo->count_group_by($terms, {%args, group => $group_exprs});

$terms and %args pick out a subset of the MT::Foo objects in the usual way. $group_expressions is an array reference containing the SQL expressions for the values you want to group by. A single row will be returned for each distinct tuple of values resulting from the $group_expressions. For example, if $group_expressions were just a single column (e.g. group => ['created_on']) then a single row would be returned for each distinct value of the created_on column. If $group_expressions were multiple columns, a row would be returned for each distinct pair (or n-tuple) of values found in those columns.

Each application of the iterator $iter returns a list in the form:

($count, $group_val1, $group_val2, ...)

Where $count is the number of MT::Foo objects for which the group expressions are the values ($group_val1, $group_val2, ...). These values are in the same order as the corresponding group expressions in the $group_exprs argument.

In this example, we load up groups of MT::Pip objects, grouped by the pair (cat_id, invoice_id), and print how many pips have that pair of values.

$iter = MT::Pip->count_group_by(undef,
                                {group => ['cat_id',
                                           'invoice_id']});
while (($count, $cat, $inv) = $iter->()) {
    print "There are $count Pips with " .
        "category $cat and invoice $inv\n";
}

Averaging by Group

Class->avg_group_by()

Like the count_group_by method, you can select groups of averages from a MT::Object store.

my $iter = MT::Foo->avg_group_by($terms, {%args, group => $group_exprs,
    avg => 'property_to_average' })

Max by Group

Class->max_group_by()

Like the count_group_by method, you can select objects from a MT::Object store using a SQL 'MAX' operator.

my $iter = MT::Foo->max_group_by($terms, {%args, group => $group_exprs,
    max => 'column_name' })

Sum by Group

Class->sum_group_by()

Like the count_group_by method, you can select groups of sums from a MT::Object store.

my $iter = MT::Foo->sum_group_by($terms, {%args, group => $group_exprs,
    avg => 'property_to_sum' })

Inspecting and Manipulating Object State

$obj->column_values()

Use column_values and set_values to get and set the fields of an object en masse. The former returns a hash reference mapping column names to their values in this object. For example:

$values = $obj->column_values()

$obj->set_values()

set_values accepts a similar hash ref, which need not give a value for every field. For example:

$obj->set_values({col1 => $val1, col2 => $val2});

is equivalent to

$obj->col1($val1);
$obj->col2($val2);

Other Methods

$obj->clone([%param])

Returns a clone of $obj. That is, a distinct object which has all the same data stored within it. Changing values within one object does not modify the other.

An optional except parameter may be provided to exclude particular columns from the cloning operation. For example, the following would clone the elements of the blog except the name attribute.

$blog->clone({ except => { name => 1 } });

$obj->clone_all()

Similar to the clone method, but also makes a clones the metadata information.

$obj->column_names()

Returns a list of the names of columns in $obj; includes all those specified to the install_properties method as well as the audit properties (created_on, modified_on, created_by, modified_by), if those were enabled in install_properties.

MT::Foo->driver()

$obj->driver()

Returns the ObjectDriver object that links this object with a database. This is a subclass of Data::ObjectDriver.

$obj->dbi_driver()

This method is similar to the 'driver' method, but will always return a DBI driver (a subclass of the Data::ObjectDriver::Driver::DBI class) and not a caching driver.

$obj->created_on_obj()

Returns a MT::DateTime object representing the moment when the object was first saved to the database.

$obj->column_as_datetime( $column )

Returns a MT::DateTime object for the specified datetime/timestamp column specified.

MT::Foo->set_by_key($key_terms, $value_terms)

A convenience method that loads whatever object matches the $key_terms argument and sets some or all of its fields according to the $value_terms. For example:

MT::Foo->set_by_key({name => 'Thor'},
                    {region => 'Norway', gender => 'Male'});

This loads the MT::Foo object having 'name' field equal to 'Thor' and sets the 'region' and 'gender' fields appropriately.

More than one term is acceptable in the $key_terms argument. The matching object is the one that matches all of the $key_terms.

This method only useful if you know that there is a unique object matching the given key. There need not be a unique constraint on the columns named in the $key_hash; but if not, you should be confident that only one object will match the key.

MT::Foo->get_by_key($key_terms)

A convenience method that loads whatever object matches the $key_terms argument. If no matching object is found, a new object will be constructed and the $key_terms provided will be assigned to it. So regardless of whether the key exists already, this method will return an object with the key requested. Note, however: if a new object is instantiated it is not automatically saved.

my $thor = MT::Foo->get_by_key({name => 'Thor'});
$thor->region('Norway');
$thor->gender('Male');
$thor->save;

The fact that it returns a new object if one isn't found is to help optimize this pattern:

my $obj = MT::Foo->load({key => $value});
if (!$obj) {
    $obj = new MT::Foo;
    $obj->key($value);
}

This is equivalent to:

my $obj = MT::Foo->get_by_key({key => $value});

If you don't appreciate the autoinstantiation behavior of this method, just use the load method instead.

More than one term is acceptable in the $key_terms argument. The matching object is the one that matches all of the $key_terms.

This method only useful if you know that there is a unique object matching the given key. There need not be a unique constraint on the columns named in the $key_hash; but if not, you should be confident that only one object will match the key.

$obj->cache_property($key, $code)

Caches the provided key (e.g. entry, trackback) with the return value of the given code reference (which is often an object load call) so that the value does not have to be recomputed each time.

$obj->clear_cache()

Clears any object-level cache data (from the cache_property method) that may existing.

$obj->column_def($name)

This method returns the value of the given $name column_defs propery.

$obj->column_defs()

This method returns all the column_defs of the property of the object.

Class->index_defs()

This method returns all the index definitions assigned to this class. This is the 'indexes' member of the properties installed for the class.

$obj->to_hash()

Returns a hashref containing column and metadata key/value pairs for the object. If the object has a blog relationship, it also populates data from that blog. For example:

my $entry_hash = $entry->to_hash();
# returns: { entry.title => "Title", entry.blog.name => "Foo", ... }

Class->join_on( $join_column, %join_terms, %join_args )

A simple helper method that returns an arrayref of join terms suitable for the load and load_iter methods.

$obj->properties()

Returns a hashref of the object properties that were declared with the install_properties method.

$obj->to_xml()

Returns an XML representation of the object. This method is defined in MT/BackupRestore.pm - you must first use MT::BackupRestore to use this method.

$obj->restore_parent_ids()

TODO - Backup file contains parent objects' ids (foreign keys). However, when parent objects are restored, their ids will be changed. This method is to match the old and new ids of parent objects for children objects to be correctly associated.

This method is defined in MT/BackupRestore.pm - you must first use MT::BackupRestore to use this method.

$obj->parent_names()

TODO - Should be overridden by subclasses to return correct hash whose keys are xml element names of the object's parent objects and values are class names of them.

This method is defined in MT/BackupRestore.pm - you must first use MT::BackupRestore to use this method.

Class->class_handler($type)

Returns the appropriate Perl package name for the given type identifier. For example,

# Yields MT::Asset::Image
MT::Asset->class_handler('asset.image');

Class->class_label

Provides a descriptive name for the requested class package. This is a localized name, using the currently assigned language.

Class->class_label_plural

Returns a descriptive pluralized name for the requested class package. This is a localized name, using the currently assigned language.

Class->class_labels

Returns a hashref of type identifiers to class labels for all subclasses associated with a multiclassed object type. For instance:

# returns { 'asset' => 'Asset', 'asset.video' => 'Video', ... }
my $labels = MT::Asset->class_labels;

Class->columns_of_type(@types)

Returns an arrayref of column names that are of the requested type.

my @dates = MT::Foo->columns_of_type('datetime', 'timestamp')

Class->has_column( $name )

Returns a boolean as to whether the column $name is defined for this class.

Class->table_name()

Returns the database table name (including any prefix) for the class.

$obj->column_func( $column )

Creates an accessor/mutator method for column $column, returning it as a coderef. This method overrides the one in Data::ObjectDriver::BaseObject, by supporting metadata column as well.

$obj->call_trigger( 'trigger_name', @params )

Issues a call to any Class::Trigger triggers installed for the given object. Also invokes any MT callbacks that are registered using MT's callback system. "pre" callbacks are invoked prior to triggers; "post" callbacks are invoked after triggers are called.

$obj->deflate

Returns a minimal representation of the object, including any metadata. See also Data::ObjectDriver::BaseObject.

Class->inflate( $deflated )

Inflates the deflated representation of the object $deflated into a proper object in the class Class. That is, undoes the operation $deflated = $obj->deflate() by returning a new object equivalent to $obj.

Class->install_pre_init_properties

This static method is used to install any class properties that were registered prior to the bootstrapping of MT plugins.

$obj->modified_by

A modified getter/setter accessor method for audited classes with a modified_by, modified_on columns. In the event this method is called to assign a modified_by value, it automatically updates the modified_on column as well.

$obj->nextprev( %params )

Method to determine adjacent objects, based on a date column and/or id. The %params hash provides the following elements:

  • direction - Either "next" or "previous".
  • terms - Any additional terms to supply to the load method.
  • args - Any additional arguments to supply to the load method (such as a join).
  • by - The column to use to determine the next/previous object. By default for audited classes, this is created_on.

NOTES

Note on object locking

When you read objects from the datastore, the object table is locked with a shared lock; when you write to the datastore, the table is locked with an exclusive lock.

Thus, note that saving or removing objects in the same loop where you are loading them from an iterator will not work - the reason is that the datastore maintains a shared lock on the object table while objects are being loaded from the iterator, and thus the attempt to gain an exclusive lock when saving or removing an object will cause deadlock.

For example, you cannot do the following:

my $iter = MT::Foo->load_iter({ foo => 'bar' });
while (my $foo = $iter->()) {
    $foo->remove;
}

Instead you should do either this:

my @foo = MT::Foo->load({ foo => 'bar' });
for my $foo (@foo) {
    $foo->remove;
}

or this:

my $iter = MT::Foo->load_iter({ foo => 'bar' });
my @to_remove;
while (my $foo = $iter->()) {
    push @to_remove, $foo
        if SOME CONDITION;
}
for my $foo (@to_remove) {
    $foo->remove;
}

This last example is useful if you will not be removing every MT::Foo object where foo equals bar, because it saves memory - only the MT::Foo objects that you will be deleting are kept in memory at the same time.

SUBCLASSING

It is possible to declare a subclass of an existing MT::Object class, one that shares the same table storage as the parent class. Examples of this include MT::Log, MT::Entry, MT::Category. In these cases, the subclass identifies a 'class_type' property. The parent class must also have a column where this identifier is stored. Upon loading records from the table, the object is reblessed into the appropriate package.

Class->add_class( $type_id, $class )

This method can be called directly to register a new subclass type and package for the base class.

MT::Foo->add_class( 'foochild' => 'MT::Foo::Subclass' );

METADATA

The following methods facilitate the storage and management of metadata; available when the 'meta' key is included in the installed properties for the class.

$obj->init_meta()

For object classes that have metadata storage, this method will initialize the metadata member.

Class->install_meta( %meta_properties )

Called to register metadata properties on a particular class. The %meta_properties may contain an arrayref of 'columns', or a hashref of 'column_defs' (similar to the install_properties method):

MT::Foo->install_meta( { column_defs => {
    'metadata1' => 'integer indexed',
    'metadata2' => 'string indexed',
} });

In this form, the storage type is explicitly declared, so the metadata is stored into the appropriate column (vinteger_idx and vchar_idx respectively).

MT::Foo->install\_meta( { columns => [ 'metadata1', 'metadata2' ] } )

In this form, the metadata properties store their data into a 'blob' column in the meta table. This type of metadata cannot be used to sort or filter on. This form is supported for backward compatibility and is considered deprecated.

$obj->remove_meta()

Deletes all related metadata for the given object.

Class->search_by_meta( $key, $value, [ %terms [, %args ] ] )

Returns objects that have a $key metadata value of $value. Further restrictions on the class may be applied through the optional %terms and %args parameters.

$obj->meta_obj()

Returns the MT::Object class

Class->meta_pkg()

Returns the Perl package name for storing it's metadata objects.

Class->meta_args

Returns the source of a Perl package declaration that is loaded to declare and process metadata objects for the Class.

Class->has_meta( [ $name ] )

Returns a boolean as to whether the class has metadata when called without a parameter, or whether there exists a metadata column of the given $name.

Class->is_meta_column( $name )

Returns a boolean as to whether the class has a meta column named $name.

CALLBACKS

$obj->add_callback()

Most MT::Object operations can trigger callbacks to plugin code. Some notable uses of this feature are: to be notified when a database record is modified, or to pre- or post- process the data being flowing to the database.

To add a callback, invoke the add_callback method of the MT::Object subclass, as follows:

MT::Foo->add_callback( "pre_save", <priority>, 
                       <plugin object>, \&callback_function);

The first argument is the name of the hook point. Any MT::Object subclass has a pre_ and a post_ hook point for each of the following operations:

  • load
  • save
  • update (issued for save on existing objects)
  • insert (issued for save on new objects)
  • remove
  • remove_all
  • (load_iter operations will call the load callbacks)

The second argument, <priority>, is the relative order in which the callback should be called. The value should be between 1 and 10, inclusive. Callbacks with priority 1 will be called before those with 2, 2 before 3, and so on.

Plugins which know they need to run first or last can use the priority values 0 and 11. A callback with priority 0 will run before all others, and if two callbacks try to use that value, an error will result. Likewise priority 11 is exclusive, and runs last.

How to remember which callback priorities are special? As you know, most guitar amps have a volume knob that goes from 1 to 10. But, like that of certain rock stars, our amp goes up to 11. A callback with priority 11 is the "loudest" or most powerful callback, as it will be called just before the object is saved to the database (in the case of a 'pre' callback), or just before the object is returned (in the case of a 'post' callback). A callback with priority 0 is the "quietest" callback, as following callbacks can completely overwhelm it. This may be a good choice for your plugin, as you may want your plugin to work well with other plugins. Determining the correct priority is a matter of thinking about your plugin in relation to others, and adjusting the priority based on experience so that users get the best use out of the plugin.

The <plugin object> is an object of type MT::Plugin which gives some information about the plugin. This is used to include the plugin's name in any error messages.

<callback function> is a code reference for a subroutine that will be called. The arguments to this function vary by operation (see MT::Callback for details), but in each case the first parameter is the MT::Callback object itself:

sub my_callback {
    my ($cb, ...) = @_;
    if ( <error condition> ) {
        return $cb->error("Error message");
    }
}

Strictly speaking, the return value of a callback is ignored. Calling the error() method of the MT::Callback object ($cb in this case) propagates the error message up to the MT activity log.

Another way to handle errors is to call die. If a callback dies, MT will warn the error to the activity log, but will continue processing the MT::Object operation: so other callbacks will still run, and the database operation should still occur.

Any-class Object Callbacks

If you add a callback to the MT class with a hook point that begins with *::, such as:

MT->add_callback('*::post_save', 7, $my_plugin, \&code_ref);

then it will be called whenever post_save callbacks are called. "Any-class" callbacks are called after all class-specific callbacks. Note that add_callback must be called on the MT class, not on a subclass of MT::Object.

Caveat: Handling Errors

Be careful how you handle errors. If you transform data as it goes into and out of the database, and it is possible for one of your callbacks to fail, the data may get saved in an undefined state. It may then be difficult or impossible for the user to recover that data.

 

      [[http://openmelody.org/assets_c/2009/06/melody-logo-mark-on-white-thumb-150x150-7.jpg|float|align=left]] 

Questions, comments, can't find something? Let us know at our community outpost on Get Satisfaction.

Credits

  • Author: Byrne Reese
  • Edited by: Violet Bliss Dietz
Clone this wiki locally