3.4 Registry Extension Guide

Extend Order Daemon with custom triggers, conditions, and actions that integrate seamlessly with the rule builder. This guide shows WordPress developers how to add their own components to create powerful, custom order management workflows.

What You Can Build

The registry system lets you add custom components that appear in the Order Daemon rule builder:

  • Custom Triggers: Define when your rules should run (e.g., “When subscription renews”, “When inventory is low”)
  • Custom Conditions: Add business logic filters (e.g., “Customer lifetime value”)
  • Custom Actions: Create automated responses (e.g., “Send to CRM”, “Update inventory”, “Notify warehouse”)

Real-World Examples:

  • Subscription management workflows
  • Inventory automation
  • Customer segmentation
  • External system integrations
  • Custom notification systems

Quick Start

Basic Setup

Register your extensions during WordPress initialization:

add_action('init', 'my_plugin_register_order_extensions');

function my_plugin_register_order_extensions() {
    // Only register if Order Daemon is active
    if (!function_exists('odcm_get_registry_instance')) {
        return;
    }
    
    $registry = odcm_get_registry_instance();
    if (!$registry) {
        return;
    }
    
    // Register your custom components
    register_my_conditions($registry);
    register_my_actions($registry);
}

Adding Custom Conditions

Conditions filter which orders your rules apply to. They’re perfect for implementing business logic and targeting specific scenarios.

Simple Condition Example

function register_my_conditions($registry) {
    $registry->register_condition([
        'id' => 'customer_lifetime_value',
        'label' => __('Customer Lifetime Value', 'my-plugin'),
        'description' => __('Target customers based on total purchase amount', 'my-plugin'),
        'capability' => 'condition_customer_ltv', // Required for entitlement system
        'render_callback' => 'render_ltv_condition'
    ]);
}

function render_ltv_condition($rule_data) {
    $operator = isset($rule_data['ltv_operator']) ? $rule_data['ltv_operator'] : 'greater_than';
    $amount = isset($rule_data['ltv_amount']) ? $rule_data['ltv_amount'] : '';
    $currency = get_woocommerce_currency_symbol();
    ?>
    <div class="odcm-field-group">
        <label><?php _e('Customer Lifetime Value:', 'my-plugin'); ?></label>
        <div class="odcm-condition-row">
            <select name="ltv_operator">
                <option value="greater_than" <?php selected($operator, 'greater_than'); ?>>
                    <?php _e('Greater than', 'my-plugin'); ?>
                </option>
                <option value="less_than" <?php selected($operator, 'less_than'); ?>>
                    <?php _e('Less than', 'my-plugin'); ?>
                </option>
            </select>
            <input type="number" 
                   name="ltv_amount" 
                   value="<?php echo esc_attr($amount); ?>" 
                   step="0.01" 
                   min="0"
                   placeholder="0.00">
            <span class="currency-symbol"><?php echo $currency; ?></span>
        </div>
    </div>
    <?php
}

Condition Evaluation Logic

Hook into the evaluation process to implement your condition logic:

add_filter('odcm_evaluate_condition', 'evaluate_ltv_condition', 10, 3);

function evaluate_ltv_condition($result, $condition_type, $condition_data) {
    // Only handle our condition type
    if ($condition_type !== 'customer_lifetime_value') {
        return $result;
    }
    
    $order_id = $condition_data['order_id'];
    $order = wc_get_order($order_id);
    if (!$order) {
        return false;
    }
    
    // Calculate customer lifetime value
    $customer_email = $order->get_billing_email();
    $ltv = calculate_customer_lifetime_value($customer_email);
    
    // Get condition settings
    $operator = isset($condition_data['ltv_operator']) ? $condition_data['ltv_operator'] : 'greater_than';
    $target_amount = floatval($condition_data['ltv_amount']);
    
    // Evaluate condition
    switch ($operator) {
        case 'greater_than':
            return $ltv > $target_amount;
        case 'less_than':
            return $ltv < $target_amount;
        default:
            return false;
    }
}

function calculate_customer_lifetime_value($email) {
    global $wpdb;
    
    $result = $wpdb->get_var($wpdb->prepare("
        SELECT SUM(pm.meta_value) 
        FROM {$wpdb->posts} p
        JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
        JOIN {$wpdb->postmeta} pm2 ON p.ID = pm2.post_id
        WHERE p.post_type = 'shop_order'
        AND p.post_status IN ('wc-completed', 'wc-processing')
        AND pm.meta_key = '_order_total'
        AND pm2.meta_key = '_billing_email'
        AND pm2.meta_value = %s
    ", $email));
    
    return floatval($result);
}

Adding Custom Actions

Actions define what happens when a rule matches – from sending notifications to updating external systems.

Simple Action Example

function register_my_actions($registry) {
    $registry->register_action([
        'id' => 'send_to_crm',
        'label' => __('Send to CRM', 'my-plugin'),
        'description' => __('Sync customer data with CRM system', 'my-plugin'),
        'capability' => 'action_crm_sync', // Required for entitlement system
        'render_callback' => 'render_crm_action'
    ]);
}

function render_crm_action($rule_data) {
    $crm_system = isset($rule_data['crm_system']) ? $rule_data['crm_system'] : 'salesforce';
    $api_key = isset($rule_data['crm_api_key']) ? $rule_data['crm_api_key'] : '';
    ?>
    <div class="odcm-field-group">
        <label for="crm_system"><?php _e('CRM System:', 'my-plugin'); ?></label>
        <select name="crm_system" id="crm_system">
            <option value="salesforce" <?php selected($crm_system, 'salesforce'); ?>>
                <?php _e('Salesforce', 'my-plugin'); ?>
            </option>
            <option value="hubspot" <?php selected($crm_system, 'hubspot'); ?>>
                <?php _e('HubSpot', 'my-plugin'); ?>
            </option>
        </select>
    </div>
    
    <div class="odcm-field-group">
        <label for="crm_api_key"><?php _e('API Key:', 'my-plugin'); ?></label>
        <input type="password" 
               name="crm_api_key" 
               id="crm_api_key"
               value="<?php echo esc_attr($api_key); ?>" 
               placeholder="<?php _e('Enter your CRM API key', 'my-plugin'); ?>">
    </div>
    <?php
}

Action Execution Logic

Hook into the execution process to implement your action:

add_action('odcm_execute_action', 'execute_crm_sync_action', 10, 3);

function execute_crm_sync_action($action_type, $action_data, $order_id) {
    // Only handle our action type
    if ($action_type !== 'send_to_crm') {
        return;
    }
    
    $order = wc_get_order($order_id);
    if (!$order) {
        odcm_log_custom_event(
            "CRM sync failed: Order #{$order_id} not found",
            ['action_type' => $action_type],
            $order_id,
            'error'
        );
        return;
    }
    
    try {
        $crm_system = $action_data['crm_system'];
        $api_key = $action_data['crm_api_key'];
        
        // Prepare customer data
        $customer_data = [
            'email' => $order->get_billing_email(),
            'first_name' => $order->get_billing_first_name(),
            'last_name' => $order->get_billing_last_name(),
            'order_total' => $order->get_total()
        ];
        
        // Send to CRM
        $result = send_to_crm($crm_system, $customer_data, $api_key);
        
        if ($result['success']) {
            odcm_log_custom_event(
                "Customer data synchronized with {$crm_system}",
                [
                    'crm_system' => $crm_system,
                    'crm_id' => $result['crm_id']
                ],
                $order_id,
                'success'
            );
        } else {
            throw new Exception($result['error']);
        }
        
    } catch (Exception $e) {
        odcm_log_custom_event(
            "CRM sync failed: " . $e->getMessage(),
            ['crm_system' => $crm_system],
            $order_id,
            'error'
        );
    }
}

Development Best Practices

Security and Validation

Always validate and sanitize user input:

function secure_render_callback($rule_data) {
    $value = isset($rule_data['my_field']) ? sanitize_text_field($rule_data['my_field']) : '';
    $number = isset($rule_data['my_number']) ? absint($rule_data['my_number']) : 0;
    
    // Render your UI with sanitized values
}

Error Handling

Implement robust error handling:

function safe_registry_registration() {
    try {
        $registry = odcm_get_registry_instance();
        if (!$registry) {
            throw new Exception('Registry not available');
        }
        
        // Your registration code here
        
    } catch (Exception $e) {
        // Log error but don't break the site
        error_log('Order Daemon extension error: ' . $e->getMessage());
    }
}

Performance Guidelines

  • Keep condition evaluation logic lightweight
  • Cache expensive operations using WordPress transients
  • Avoid database queries in frequently called functions
  • Use appropriate hook priorities

Compatibility Checks

Always check for dependencies:

function register_my_extensions() {
    // Check if required plugins are active
    if (!class_exists('WooCommerce') || !function_exists('odcm_get_registry_instance')) {
        return;
    }
    
    // Safe to register extensions
    register_my_conditions();
    register_my_actions();
}

Testing and Debugging

Development Setup

Enable debugging for development:

// In wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('ODCM_DEBUG', true);

Testing Checklist

  1. Registration: Verify components appear in rule builder
  2. UI Rendering: Test settings forms with various inputs
  3. Validation: Test with invalid input
  4. Execution: Verify logic works with real orders
  5. Error Handling: Test failure scenarios
  6. Performance: Monitor execution time

Debug Logging

Use Order Daemon’s logging system for debugging:

// Log debug information
odcm_log_custom_event(
    'Extension debug: Processing order',
    [
        'extension' => 'my-plugin',
        'order_id' => $order_id,
        'debug_data' => $debug_info
    ],
    $order_id,
    'info'
);

Example: Complete Extension

Here’s a complete example that demonstrates all concepts:

<?php
/**
 * Order Daemon Extension: Advanced Customer Segmentation
 */

class ODCM_Customer_Segmentation_Extension {
    
    public function __construct() {
        add_action('init', [$this, 'register_extensions']);
        add_filter('odcm_evaluate_condition', [$this, 'evaluate_segment_condition'], 10, 3);
        add_action('odcm_execute_action', [$this, 'execute_segment_action'], 10, 3);
    }
    
    public function register_extensions() {
        if (!function_exists('odcm_get_registry_instance')) {
            return;
        }
        
        $registry = odcm_get_registry_instance();
        if (!$registry) {
            return;
        }
        
        // Register condition
        $registry->register_condition([
            'id' => 'customer_segment',
            'label' => __('Customer Segment', 'customer-segmentation'),
            'description' => __('Check if customer belongs to specific segment', 'customer-segmentation'),
            'section' => 'addon',
            'render_callback' => [$this, 'render_segment_condition']
        ]);
        
        // Register action
        $registry->register_action([
            'id' => 'assign_segment',
            'label' => __('Assign Customer Segment', 'customer-segmentation'),
            'description' => __('Assign customer to a specific segment', 'customer-segmentation'),
            'section' => 'addon',
            'render_callback' => [$this, 'render_segment_action']
        ]);
    }
    
    public function render_segment_condition($rule_data) {
        $segments = $this->get_available_segments();
        $selected_segment = isset($rule_data['target_segment']) ? $rule_data['target_segment'] : '';
        ?>
        <div class="odcm-field-group">
            <label for="target_segment"><?php _e('Customer Segment:', 'customer-segmentation'); ?></label>
            <select name="target_segment" id="target_segment">
                <option value=""><?php _e('Select segment...', 'customer-segmentation'); ?></option>
                <?php foreach ($segments as $id => $label): ?>
                    <option value="<?php echo esc_attr($id); ?>" <?php selected($selected_segment, $id); ?>>
                        <?php echo esc_html($label); ?>
                    </option>
                <?php endforeach; ?>
            </select>
        </div>
        <?php
    }
    
    public function render_segment_action($rule_data) {
        $segments = $this->get_available_segments();
        $target_segment = isset($rule_data['assign_segment']) ? $rule_data['assign_segment'] : '';
        ?>
        <div class="odcm-field-group">
            <label for="assign_segment"><?php _e('Assign to Segment:', 'customer-segmentation'); ?></label>
            <select name="assign_segment" id="assign_segment">
                <option value=""><?php _e('Select segment...', 'customer-segmentation'); ?></option>
                <?php foreach ($segments as $id => $label): ?>
                    <option value="<?php echo esc_attr($id); ?>" <?php selected($target_segment, $id); ?>>
                        <?php echo esc_html($label); ?>
                    </option>
                <?php endforeach; ?>
            </select>
        </div>
        <?php
    }
    
    public function evaluate_segment_condition($result, $condition_type, $condition_data) {
        if ($condition_type !== 'customer_segment') {
            return $result;
        }
        
        $order_id = $condition_data['order_id'];
        $order = wc_get_order($order_id);
        if (!$order) {
            return false;
        }
        
        $customer_email = $order->get_billing_email();
        $customer_segment = $this->get_customer_segment($customer_email);
        $target_segment = $condition_data['target_segment'];
        
        return $customer_segment === $target_segment;
    }
    
    public function execute_segment_action($action_type, $action_data, $order_id) {
        if ($action_type !== 'assign_segment') {
            return;
        }
        
        $order = wc_get_order($order_id);
        if (!$order) {
            return;
        }
        
        $customer_email = $order->get_billing_email();
        $target_segment = $action_data['assign_segment'];
        
        try {
            $this->assign_customer_segment($customer_email, $target_segment);
            
            odcm_log_custom_event(
                "Customer assigned to segment: {$target_segment}",
                [
                    'customer_email' => $customer_email,
                    'segment' => $target_segment,
                    'order_id' => $order_id
                ],
                $order_id,
                'success'
            );
            
        } catch (Exception $e) {
            odcm_log_custom_event(
                "Failed to assign customer segment: " . $e->getMessage(),
                [
                    'customer_email' => $customer_email,
                    'target_segment' => $target_segment,
                    'error' => $e->getMessage()
                ],
                $order_id,
                'error'
            );
        }
    }
    
    private function get_available_segments() {
        return [
            'vip' => __('VIP Customer', 'customer-segmentation'),
            'regular' => __('Regular Customer', 'customer-segmentation'),
            'new' => __('New Customer', 'customer-segmentation'),
            'at_risk' => __('At Risk', 'customer-segmentation')
        ];
    }
    
    private function get_customer_segment($email) {
        return get_user_meta(get_user_by('email', $email)->ID, 'customer_segment', true);
    }
    
    private function assign_customer_segment($email, $segment) {
        $user = get_user_by('email', $email);
        if ($user) {
            update_user_meta($user->ID, 'customer_segment', $segment);
        }
    }
}

// Initialize the extension
new ODCM_Customer_Segmentation_Extension();

This registry extension system provides a powerful foundation for extending Order Daemon with custom functionality while maintaining consistency, security, and performance standards.

Was this article helpful?

  • Loading...
Table of Contents
  • Loading...