WP Manifestindependent plugin directory
manifest / developer / datastore

Hizzle Data Stores

Hizzle Data Stores

★ 3stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/hizzle-co/datastore/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

This is currently in Beta so expect the API to change alot.

Installation

Install via Composer:

composer require hizzle/store

Features

  • CRUD Operations: Create, read, update, and delete records
  • Query Builder: Powerful query builder with filtering, sorting, and pagination
  • Aggregate Functions: Support for SUM, AVG, COUNT, MIN, MAX with grouping
  • JOIN Queries: Relate collections together for complex data analysis
  • REST API: Automatic REST API endpoints for all collections
  • Meta Fields: Support for custom meta fields with multiple values
  • Custom Post Types: Integrate with WordPress custom post types

Quick Start

Recommended: Using Main Class

The Main class provides a simplified API for interacting with your store:

use Hizzle\Store\Main;

// Get or create a store instance with an optional default collection.
$db = Main::instance('my_store', 'orders');

// Initialize store with collections
$db->init_store(
    array(
        'orders' => array(
            'object'        => 'Order',
            'singular_name' => 'order',
            'props'         => array(
                'id'          => array(
                    'type'        => 'BIGINT',
                    'length'      => 20,
                    'nullable'    => false,
                    'extra'       => 'AUTO_INCREMENT',
                    'description' => 'Order ID',
                ),
                'customer_id' => array(
                    'type'        => 'BIGINT',
                    'length'      => 20,
                    'nullable'    => false,
                    'description' => 'Customer ID',
                ),
                'total'       => array(
                    'type'        => 'DECIMAL',
                    'length'      => '10,2',
                    'nullable'    => false,
                    'description' => 'Order total',
                ),
                'status'      => array(
                    'type'        => 'VARCHAR',
                    'length'      => 20,
                    'default'     => 'pending',
                    'description' => 'Order status',
                ),
            ),
            'keys'          => array(
                'primary'     => array( 'id' ),
                'customer_id' => array( 'customer_id' ),
                'status'      => array( 'status' ),
            ),
            'labels'        => array(
                'name'          => __( 'Orders', 'textdomain' ),
                'singular_name' => __( 'Order', 'textdomain' ),
            ),
        ),
    )
);

// Work with records
$order = $db->get(123);
$orders = $db->query('orders', array('status' => 'completed'));

Alternative: Using Store Class Directly

use Hizzle\Store\Store;

// Initialize a store
$store = new Store(
    'my_store',
    array(
        'payments' => array(
            // This object must extend Hizzle\Store\Record
            'object'        => 'Payment',
            'singular_name' => 'payment',
            'props'         => array(
                'id'                  => array(
                    'type'        => 'BIGINT',
                    'length'      => 20,
                    'nullable'    => false,
                    'extra'       => 'AUTO_INCREMENT',
                    'description' => 'Payment ID',
                ),
                'customer_id'       => array(
                    'type'        => 'BIGINT',
                    'length'      => 20,
                    'nullable'    => false,
                    'description' => 'Customer ID',
                ),
                /* ... */
            ),
            'joins'         => array(
                'customers' => array(
                    'collection' => 'my_store_customers',
                    'on'         => 'customer_id',
                    'type'       => 'LEFT',
                ),
                'plans'     => array(
                    'collection' => 'my_store_plans',
                    'on'         => 'plan_id',
                    'type'       => 'LEFT',
                ),
                'products'  => array(
                    'collection' => 'my_store_products',
                    // We are assuming that the above payments schema has
                    // No 'plan_id' property, so we join via plans table
                    // Which is already joined above
                    'on'         => 'plans.product_id',
                    'type'       => 'LEFT',
                ),
            ),
            'keys'          => array(
                'primary'             => array( 'id' ),
                'customer_id'         => array( 'customer_id' ),
                'subscription_id'     => array( 'subscription_id' ),
                'status'              => array( 'status' ),
                'date_created_status' => array( 'date_created', 'status' ),
                'unique'              => array( 'uuid', 'transaction_id' ),
            ),
            'labels'        => array(
                'name'          => __( 'Payments', 'textdomain' ),
                'singular_name' => __( 'Payment', 'textdomain' ),
                'add_new'       => __( 'Add New', 'textdomain' ),
                'add_new_item'  => __( 'Add New Payment', 'textdomain' ),
                'edit_item'     => __( 'Overview', 'textdomain' ),
                'new_item'      => __( 'Add Payment', 'textdomain' ),
                'view_item'     => __( 'View Payment', 'textdomain' ),
                'view_items'    => __( 'View Payments', 'textdomain' ),
                'search_items'  => __( 'Search payments', 'textdomain' ),
                'not_found'     => __( 'No payments found.', 'textdomain' ),
                'import'        => __( 'Import Payments', 'textdomain' ),
            ),
        ),
        'customers' => array( /* ... */ ),
        /** Other collections **/
    )
);

Working with Records

Create Records

// Using Main class (recommended)
$db = Main::instance('my_store');

// Get the collection first
$collection = Store::instance('my_store')->get('payments');

// Create a new payment
$payment = $collection->create(array(
    'customer_id' => 123,
    'amount' => 99.99,
    'status' => 'completed',
));

// Get the payment ID
$payment_id = $payment->get_id();

Read Records

// Using Main class (recommended)
$db = Main::instance('my_store');

// Get a single record by ID
$payment = $db->get($payment_id, 'payments');

if ($payment && !is_wp_error($payment)) {
    echo $payment->get('amount'); // 99.99
    echo $payment->get('status'); // completed
}

// Get ID by a specific property
$payment_id = $db->get_id_by_prop('transaction_id', 'txn_abc123', 'payments');

// Using Collection directly
// Throws \Hizzle\Store\Store_Exception on failure
$collection = Store::instance('my_store')->get('payments');
$payment = $collection->get($payment_id);

// Check if a record exists
if ($collection->exists($payment_id)) {
    // Record exists
}

Update Records

// Get the record using Main class
$db = Main::instance('my_store');
$payment = $db->get($payment_id, 'payments');

if ($payment && !is_wp_error($payment)) {
    $payment->set('status', 'refunded');
    $payment->set('refund_date', current_time('mysql'));
    $payment->save();
}

// Or update via collection
// Throws \Hizzle\Store\Store_Exception on failure
$collection = Store::instance('my_store')->get('payments');
$collection->update($payment_id, array(
    'status' => 'refunded',
    'refund_date' => current_time('mysql'),
));

Delete Records

// Using Main class (recommended)
$db = Main::instance('my_store');

// Delete records matching criteria
$deleted = $db->delete_where(
    array(
        'status' => 'pending',
        'customer_id' => 123,
    ),
    'payments'
);

// Delete all records (use with caution!)
$db->delete_all('payments');

// Or delete via record object
$payment = $db->get($payment_id, 'payments');
if ($payment && !is_wp_error($payment)) {
    $payment->delete();
}

Querying Records

// Using Main class (recommended)
$db = Main::instance('my_store');

// Basic query - returns results
$payments = $db->query('payments', array(
    'status' => 'completed',
    'customer_id' => 123,
    'per_page' => 10,
    'page' => 1,
));

// Count records
$count = $db->query('payments', array(
    'status' => 'completed',
), 'count');

// Aggregate query
$results = $db->query('payments', array(
    'aggregate' => array(
        'amount' => array('SUM', 'AVG', 'COUNT'),
    ),
    'groupby' => 'status',
), 'aggregate');

// Get Query object for more control
$query = $db->query('payments', array(
    'status' => 'completed',
), 'query');

$payments = $query->get_results();
$total = $query->get_total();

// Using Collection directly
$collection = Store::instance('my_store')->get('payments');
$query = $collection->query(array(
    'status' => 'completed',
    'customer_id' => 123,
    'per_page' => 10,
    'page' => 1,
));

$payments = $query->get_results();
$total = $query->get_total();

// Complex query with date filters
$payments = $db->query('payments', array(
    'status' => array('completed', 'pending'),
    'amount_min' => 50,
    'date_created_after' => '2026-01-01',
    'orderby' => 'date_created',
    'order' => 'DESC',
));

Working with Metadata

// Using Main class (recommended)
$db = Main::instance('my_store');

// Add meta data
$db->add_record_meta($payment_id, 'gateway', 'stripe', false, 'payments');

// Get meta data
$gateway = $db->get_record_meta($payment_id, 'gateway', true, 'payments');

// Update meta data
$db->update_record_meta($payment_id, 'gateway', 'paypal', '', 'payments');

// Delete meta data
$db->delete_record_meta($payment_id, 'gateway', '', 'payments');

// Delete all metadata for a record
$db->delete_all_record_meta($payment_id, 'payments');

// Delete all metadata by key across all records
$db->delete_all_meta_by_key('old_field', 'payments');

// Check if meta exists
if ($db->record_meta_exists($payment_id, 'gateway', 'payments')) {
    // Meta exists
}

// Using Collection directly
$collection = Store::instance('my_store')->get('payments');
$collection->add_record_meta($payment_id, 'gateway', 'stripe');
$gateway = $collection->get_record_meta($payment_id, 'gateway', true);
$collection->update_record_meta($payment_id, 'gateway', 'paypal');
$collection->delete_record_meta($payment_id, 'gateway');

Error Handling

use Hizzle\Store\Main;

// Main class automatically converts exceptions to WP_Error
$db = Main::instance('my_store');
$payment = $db->get($payment_id, 'payments');

if (is_wp_error($payment)) {
    error_log($payment->get_error_message());
} else {
    // Work with the payment
    echo $payment->get('amount');
}

// When using Store/Collection directly, use try/catch
try {
    $collection = Store::instance('my_store')->get('payments');
    $payment = $collection->get($payment_id);

    // Do something with the payment

} catch (\Hizzle\Store\Store_Exception $e) {
    error_log($e->getMessage());

    // Or convert to WP_Error
    $error = new WP_Error(
        $e->getErrorCode(),
        $e->getMessage(),
        $e->getErrorData()
    );
}

JOIN Queries

Define relationships between collections:

'customers' => array(
    'status' => 'complete',
    // ... other config
    'joins' => array(
        'payments' => array(
            'collection' => 'my_store_payments',
            'on' => 'id',
            'foreign_key' => 'customer_id',
            'type' => 'LEFT',
        ),
    ),
)

Use JOINs in aggregate queries:

$query = $collection->query(array(
    'join' => array('payments'),
    'aggregate' => array(
        'payments.amount' => array('SUM', 'COUNT'),
    ),
    'groupby' => 'id',
));

Documentation

API Reference

Complete documentation for all components is available in the docs folder:

Guides

Requirements

  • PHP >= 5.3.0
  • WordPress >= 4.7.0