1. Home
  2. Docs
  3. Application Framework
  4. Classes & Patterns
  5. Active Records

Active Records

MWP\Framework\Pattern\ActiveRecord

The active record design pattern allows rows from the database to be created, loaded, updated, and deleted using an object model. When you have custom data to manage in your plugin which requires its own database table for storage, create a class that extends MWP\Framework\Pattern\ActiveRecord to manage the database records.

@Record Customization

You can customize the protected static attributes inside your ActiveRecord class to customize which database table it is associated with and the columns needed within that table to store the record data.

namespace VendorName\PackageName;
use MWP\Framework\Pattern\ActiveRecord;

class WidgetRecord extends ActiveRecord
{
    /**
     * @var    array        Required for all active record classes
     */
    protected static $multitons = array();

    /**
     * @var    string        Table name
     */
    protected static $table = "acme_widgets";

    /**
     * @var    array        Table columns
     */
    protected static $columns = array(
        'id',
        'title' => [ 'type' => 'string', 'length' => 56 ],
        'manufacturer' => [ 'type' => 'string', 'length' => 255, 'allow_null' => true ],
        'upc_code' => [ 'type' => 'string', 'length' => 255 ],
        'price' => [ 'type' => 'float', 'length' => 10, 'decimals' => 2 ],
        'release_date' => [ 'type' => 'timestamp' ],
        'data' => [ 'type' => 'text', 'format' => 'JSON' ],
    );

    /**
     * @var    string        Table primary key
     */
    protected static $key = 'id';

    /**
     * @var    string        Table column prefix
     */
    protected static $prefix = '';

    /**
     * @var bool        Separate table per site?
     */
    protected static $site_specific = FALSE;

    /**
     * @var string      The class of the managing plugin
     */
    protected static $plugin_class = 'VendorName\PackageName\Plugin';

}

@Column Definitions

Note: When specifying the columns for your active record, the only thing that is required is the name of the column.

  • If the column name is the only thing provided, its data type will default to a VARCHAR(255) database column.
  • The primary key column for your record is automatically defined as a BIGINT(20) database column.

If you wish to customize the data type of your columns further, you can provide the column definition as an array value, with the key being the column name, and the array containing any of the following allowed parameters which customize the way the database column is deployed:

  • type: (string) – MySQL column type (default: varchar)
  • length: (int) – The length of the column (default: 255)
  • decimals: (int) – Number of decimal places for float type columns (default: 0)
  • values: (array) – Allowed values for ENUM or SET column types (default: [])
  • unsigned: (bool) – Indicate if only positive values are allowed (default: false)
  • allow_null: (bool) – Allow the column to have a NULL value (default: true)
  • auto_increment: (bool) – Auto-increment the column, primary key only (default: false)
  • binary: (bool) – Column contains binary data (default: false)
  • format: (array) – Special formatting options for column data, see below (default: null)
  • class: (string) – The class name when the format parameter is set to ActiveRecord (default: null)

protected static $columns = array( 
    'id',      // bigint(20)
    'title',   // varchar(255)
    'price' => [ 
        'type' => 'float', 
        'length' => 10, 
        'decimals' => 2 
    ], 
    'status' => [ 
        'type' => 'enum', 
        'values' => [ 'enabled', 'disabled' ], 
        'default' => 'enabled', 
        'allow_null' => false 
    ],
    'data' => [ 
        'type' => 'text', 
        'format' => 'JSON' 
    ],
    'category' => [ 
        'type' => 'int', 
        'length' => 12, 
        'format' => 'ActiveRecord', 
        'class' => 'VendorName\PackageName\Models\Category' 
    ] 
);

Format

The format column parameter allows for the data to undergo a conversion process when getting/setting the data on the active record. This means that the underlying data is stored as a primitive data type, but is automatically converted into a complex data type when accessed via its property accessor on the active record. For example, an array is a complex data type which can be automatically json encoded when being saved to the database.

The following formatting parameters are supported:

  • JSON: The data is automatically json encoded when saved to the database, and json decoded when accessed via the record property.
  • ActiveRecord: The data is saved as an active record id integer, and loaded as an object when accessed via the record property (requires class param to be set).

@Schema Tracking

After you have created your active record class and specified its column definitions, you can automatically create the appropriate database table and track it in your plugin by using the deploy-table CLI command.

Once your plugin is tracking your database table, any changes to its schema will be automatically deployed/upgraded on client systems when they install your built plugin. You can make changes to your database columns manually, or use the deploy-table CLI command at any time to update the table according to your current columns definition inside your active record class.


@Example Code

Performing crud operations on your data is fairly trivial.

use VenderName\PackageName\WidgetRecord;

// Create a new record
$widget = new WidgetRecord;
$widget->title = "Vaporizer";
$widget->manufacturer = "Acme";
$widget->upc_code = "abc123";
$widget->price = 9.99;
$widget->data = array(
    'tags' => [ 'ray gun', 'mass', 'destruction' ],
);
$widget->save();

$widget_id = $widget->id;

// Load a record by id
try {
    $otherWidget = WidgetRecord::load( 2 );
}
catch( \OutOfRangeException $e )
{
    // Whoops. Record does not exist...
}

// Load multiple records by criteria
$widgets = WidgetRecord::loadWhere( array( "manufacturer=%s", "Acme" ) );
foreach( $widgets as $w ) {
    echo $w->title . '<br>';
    echo '$' . $w->price . '<br>';
    echo '<ul>';
    foreach( $w->data['tags'] as $tag ) {
        echo '<li>' . esc_html( $tag ) . '</li>';
    }
}

// Count records
$total = WidgetRecord::countWhere( array( "manufacturer=%s", "Acme" ) );

// Delete an individual record
$widget->delete();

// Delete multiple records
WidgetRecord::deleteWhere( array( "manufacturer=%s", "Acme" ) );

@Static Property Reference

static::$multitons (required) Must be defined in your class for internal use
/**
 * @var array    Multitons cache
 */
protected static $multitons = array();

static::$table (required) Name of database table holding record rows
/**
 * @var string    Database table name
 */
protected static $table;

static::$columns (required) An array of the columns in the database table
/**
 * @var array    Table columns
 */
protected static $columns = array();

static::$key (required) The column which acts as the primary key
/**
 * @var string      Table primary key
 */
protected static $key;

static::$prefix (optional) The prefix to attach to all columns when saving data
/**
 * @var string      Table column prefix
 */
protected static $prefix = '';

A prefix allows you to read and write property values on the object without the prefix, but when the record is saved, the prefix will be added to each property name.

For example, if all of the columns in your database table have a prefix of ‘record_’, then ‘record_id’ would be accessible on the object as $object->id.


static::$site_specific (optional) Override to indicate a separate table exists for every site in a multisite
/**
 * @var bool    Multisite table?
 */
protected static $site_specific = FALSE;

If a different table exists for every site in a multisite, then this property must be set to TRUE.


static::$plugin_class (required) The classname of the plugin that manages the active record
/**
 * @var string
 */
protected static $plugin_class = 'MWP\Framework\Framework';

The plugin_class needs to name the class of the plugin which manages the active record. It is used to access templates and other plugin resources.


static::$sequence_col (optional) Column which is used to maintain record sequencing
/**
 * @var string
 */
protected static $sequence_col;

static::$parent_col (optional) Column which is used to maintain record organization
/**
 * @var string
 */
protected static $parent_col;

@Language String Properties

Override any of the following language string properties in your active record class to customize the language used when describing them.

static::$lang_singular (optional) Language used when referring to a record in the singular
/**
 * @var string
 */
public static $lang_singular = 'Record';

static::$lang_plural (optional) Language used when referring to records in the plural
/**
 * @var string
 */
public static $lang_plural = 'Records';

static::$lang_view (optional) Language used for viewing a record
/**
 * @var string
 */
public static $lang_view = 'View';

static::$lang_create
/**
 * @var string
 */
public static $lang_create = 'Create';

static::$lang_edit
/**
 * @var string
 */
public static $lang_edit = 'Edit';

static::$lang_delete
/**
 * @var string
 */
public static $lang_delete = 'Delete';

@Static Method Reference

ActiveRecord::load( $id )
/**
 * Load record by id
 *
 * @param   int     $id         Record id
 * @return  ActiveRecord
 * @throws  OutOfRangeException     Throws exception if record could not be located
 */
public static function load( $id )

ActiveRecord::loadWhere( $where, $order=NULL, $limit=NULL )
/**
 * Load multiple records
 *
 * @param  array|string  $where   Where clause with associated replacement values
 * @param  string        $order   Order by ( include field + ASC or DESC ) ex. "field_name DESC"
 * @param  int|array     $limit   Limit clause. 
 *                                If an integer is provided, it should be the number of records to limit by
 *                                If an array is provided, the first value is the offset, and the second value is the limit
 * @return  array
 */
public static function loadWhere( $where, $order=NULL, $limit=NULL )

ActiveRecord::countWhere( $where )
/**
 * Count records
 *
 * @param   array|string  $where   Where clause with associated replacement values
 * @return  array
 */
public static function countWhere( $where )

ActiveRecord::deleteWhere( $where )
/**
 * Delete records
 *
 * @param   array|string   $where   Where clause with associated replacement values
 * @return  int             Number of rows affected
 */
public static function deleteWhere( $where )

ActiveRecord::createForm( $type, $options )
/**
 * Create a new form
 *
 * @param   string        $type         An arbitrary value used to help identify the purpose of the form
 * @param   array         $options      Options to pass to the created form
 * @return  MWP\Framework\Helpers\Form
 */
public static function createForm( $type='', $options=[] )

This method is used to create a new blank form.

More information is available about form building on the Forms page.


ActiveRecord::createDisplayTable( $config )
/**
 * Create a table for viewing active records
 *
 * @param   array              $config       Table configuration parameters
 * @return  ActiveRecordTable
 */
public static function createDisplayTable( $config=[] )

More information regarding the usage and behavior of active record tables is available on the Active Record Table page.


ActiveRecord::setTableClass( $class )
/**
 * Set a custom table class for an active record
 *
 * @param   string      $class         The table class to use when creating tables
 * @return  void
 */
public static setTableClass( $class )

This will set a custom table class to be used when creating display tables for the active record.

Custom table classes must be a subclass of MWP\Framework\Helpers\ActiveRecordTable.


ActiveRecord::getTableClass()
/**
 * Get the current table class being used for an active record
 *
 * @return  string
 */
public static getTableClass()   

ActiveRecord::createController( $key, $config)
/**
 * Create a controller which can be used to interface with this active record class
 *
 * @param   string      $key          The controller key used to access this controller
 * @param   array       $config       Optional configuration options passed to controller
 * @return  ActiveRecordController
 */
public static function createController( $key, $config=[] )

When creating a controller for an active record, the controller is cached internally based on the key provided. It can then be retrieved later using ActiveRecord::getController( $key ).

Behavior of the controller is documented on the Active Record Controller page.

use Acme\Project\Models;

Models\Product::createController('admin', [
    /**
     * The adminPage config property allows you to have an admin page
     * created for administrating your model records.
     */
    'adminPage' => [
        /* Title of the page */
        'title' => __( 'Product Inventory' ),

        /* Title of the menu link */
        'menu' => __( 'Product Inventory' ),

        /**
         * Corresponds to a WP menu type
         *
         * 'menu', 'submenu', 'dashboard', 'posts', 'media', 'pages', 
         * 'comments','theme','plugins','users','management'
         */
        'type' => 'menu',

        /**
         * If the type is 'menu', this option allows the sublink to the page 
         * to have a different text
         */
        'menu_submenu' => __( 'Products' ),

        /* Menu link icon */
        'icon' => 'dashicons-vault',

        /* If the type is 'submenu', this allows the parent slug to be specified */
        'parent' => '',
    ],
]);

ActiveRecord::getController( $key )
/**
 * Get a created controller by key
 *
 * @param   string      $key             The controller key to get
 * @return  ActiveRecordController|NULL
 */
public static function getController( $key )

Controllers that have been created previously using ActiveRecord::createController() can be retrieved using the key that was used to create them.

Behavior of the controller is documented on the Active Record Controller page.


ActiveRecord::setControllerClass( $class )
/**
 * Set a custom controller class for an active record
 *
 * @param   string      $class         The controller class to use when creating controllers
 * @return  void
 */
public static setControllerClass( $class )

This will set a custom controller class to be used when creating new controllers for the active record.

Custom controller classes must be a subclass of MWP\Framework\Helpers\ActiveRecordController.


ActiveRecord::getControllerClass()
/**
 * Get the controller class for an active record
 *
 * @return  string
 */
public static getControllerClass()

@Instance Method Reference

$instance->getForm( $type )
/**
 * Get a specific type of form
 *
 * @param   string       $type           The type of form to get
 * @return  MWP\Framework\Helpers\Form
 */
public function getForm( $type='edit' )

This method is used by active record controllers get a built form for the active record. The type is used to differentiate between different forms that may be available for an active record. When a type is specified, then an associated buildTypeForm() method on the active record will be called to build the requested form.

For example: $form = $record->getForm('edit') will result in the buildEditForm() method being called (if it exists) on the active record to create the form. If the build method does not exist, then a blank form is returned.

More information is available about form building on the Forms page.


$instance->processForm( $values, $type )
/**
 * Process submitted form values 
 *
 * @param   array       $values             Submitted form values
 * @param       string          $type               The type of the form values being processed
 * @return  void
 */
public function processForm( $values, $type='edit' )

This method is used by active record controllers to process submitted form values. The type is used to differentiate between different forms that may be available for an active record. When a type is specified, then an associated processTypeForm() method on the active record will be called to process the given form values.

For example: $form = $record->processForm( $values, 'edit' ) will result in the processEditForm( $values ) method being called (if it exists) on the active record to process the form values. If a special processing method does not exist for the form type, then no processing is performed by default.

More information is available about form building on the Forms page.


$instance->getControllerActions()
/**
 * Get controller actions
 *
 * @return  array
 */
public function getControllerActions()

Overriding this method will allow you to customized the action links displayed for each individual record as it is displayed in an ActiveRecordTable.

/**
 * Get controller actions
 *
 * @return  array
 */
public function getControllerActions()
{
    return array(
        'view' => array(
            'title' => $this->_getViewTitle(),
            'icon' => 'glyphicon glyphicon-eye-open',
            'params' => array(
                'do' => 'view',
                'id' => $this->id(),
            ),
        ),
        'edit' => array(
            'title' => $this->_getEditTitle(),
            'icon' => 'glyphicon glyphicon-pencil',
            'params' => array(
                'do' => 'edit',
                'id' => $this->id(),
            ),
        ),
        'delete' => array(
            'separator' => true,
            'title' => $this->_getDeleteTitle(),
            'icon' => 'glyphicon glyphicon-trash',
            'attr' => array( 
                'class' => 'text-danger',
            ),
            'params' => array(
                'do' => 'delete',
                'id' => $this->id(),
            ),
        )
    );
}

$instance->save()
/**
 * Save the record
 *
 * @return  bool|WP_Error
 */
public function save()

$instance->delete()
/**
 * Delete a record
 *
 * @return  bool|WP_Error
 */
public function delete()

$instance->dataArray()
/**
 * Get an array of the internal record data
 *
 * @return  array
 */
public function dataArray()
Was this article helpful to you? Yes No

How can we help?