Skip to main content
Version: 1.x
Models · v1.x

Database Table Models

Most Mantle models represent a WordPress object such as a post, term, or user. When your application stores data in its own database table, you can extend Database_Table_Model to get the same model API for reading and writing rows in that table.

Defining a Database Table Model​

Extend Mantle\Database\Model\Database_Table_Model and return the name of your table from get_table_name(). The table name should not include the WordPress table prefix; Mantle prepends $wpdb->prefix for you.

namespace App\Models;

use Mantle\Database\Model\Database_Table_Model;

/**
* Subscriber Model.
*/
class Subscriber extends Database_Table_Model {
/**
* The table name for the model.
*/
public static function get_table_name(): string {
return 'subscribers';
}
}

The model above reads and writes rows in the wp_subscribers table (assuming a wp_ prefix).

If you don't override get_table_name(), the table name is the snake-cased class name. A Subscriber class maps to subscriber, and NewsletterSubscriber maps to newsletter_subscriber.

warning

The default table name is derived by inserting an underscore before every capital letter. A class following WordPress naming conventions like Newsletter_Subscriber would map to newsletter__subscriber (with a double underscore). Override get_table_name() on those models.

Creating the Table​

Mantle does not create or migrate the table for you. Create it as you normally would in WordPress, such as with dbDelta() on plugin activation:

global $wpdb;

require_once ABSPATH . 'wp-admin/includes/upgrade.php';

dbDelta(
"CREATE TABLE {$wpdb->prefix}subscribers (
id bigint unsigned NOT NULL AUTO_INCREMENT,
email varchar(255) NOT NULL,
status varchar(20) NOT NULL DEFAULT 'pending',
created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) {$wpdb->get_charset_collate()};"
);

Primary Key​

Models assume the table's primary key is an auto-incrementing id column. If your table uses a different column, override the static $primary_key property:

class Subscriber extends Database_Table_Model {
protected static string $primary_key = 'subscriber_id';
}

Creating, Updating, and Deleting Rows​

Database table models support the same create(), save(), and delete() methods as other models. Attributes map directly to the table's columns.

use App\Models\Subscriber;

$subscriber = Subscriber::create( [
'email' => '[email protected]',
] );

// The primary key is set from the inserted row.
$subscriber->id;

$subscriber->status = 'confirmed';
$subscriber->save();

$subscriber->delete();

A Mantle\Database\Model\Model_Exception is thrown if the insert, update, or delete query fails.

Querying Rows​

Retrieve a single row by its primary key with find():

$subscriber = Subscriber::find( 1 );

Database table models use Mantle\Database\Query\Database_Query_Builder, which builds SQL against the model's table and returns a collection of models. It supports a subset of the query builder:

// Retrieve all confirmed subscribers.
$subscribers = Subscriber::where( 'status', 'confirmed' )->get();

// Match multiple columns.
$subscriber = Subscriber::where( [
'email' => '[email protected]',
'status' => 'confirmed',
] )->first();

// Count matching rows.
$count = Subscriber::where( 'status', 'pending' )->count();

// Order and limit results.
$latest = Subscriber::query()
->orderBy( 'created_at', 'desc' )
->take( 10 )
->get();

where() always compares with =. Use where_raw() to compare with a different operator or to join a clause with OR:

$recent = Subscriber::query()
->where_raw( 'created_at', '>=', '2025-01-01 00:00:00' )
->where_raw( 'status', '=', 'confirmed', 'OR' )
->get();

Values are passed through $wpdb->prepare(), but column names and operators are not escaped. Never pass user input as a column name or operator.

You can limit the columns that are selected with select():

$emails = Subscriber::query()->select( [ 'id', 'email' ] )->get();

To see the SQL a query will run, call dumpSql() or ddSql():

Subscriber::where( 'status', 'confirmed' )->dumpSql();

Testing​

Use assertDatabaseHas() and assertDatabaseDoesNotHave() to check the rows in your table from a test:

use App\Models\Subscriber;

Subscriber::create( [ 'email' => '[email protected]' ] );

$this->assertDatabaseHas( Subscriber::get_table_name(), [
'email' => '[email protected]',
] );