Build your first custom rule component end-to-end in about 15 minutes.
What you’ll build: a custom Condition that checks whether an order was placed in a specific WooCommerce shipping zone – a real use case not covered by the built-in components.
Prerequisites:
- Order Daemon Core active on a WP/WooCommerce dev site
- PHP 8.x, Composer, a plugin or mu-plugin to put your code in
1. Understand the component interfaces
Every rule component implements ComponentInterface:
interface ComponentInterface {
public function get_id(): string; // Unique string key, e.g. 'my_condition'
public function get_label(): string; // Human-readable label shown in the Rule Builder UI
public function get_description(): string; // Short description shown in tooltips
public function get_settings_schema(): ?array; // JSON Schema for the UI settings form; null or [] if no settings
}Conditions additionally implement ConditionInterface:
interface ConditionInterface extends ComponentInterface {
public function evaluate(WC_Order $order, array $settings): bool;
}Triggers implement TriggerInterface, actions follow ComponentInterface with an execute() method.
2. Create the condition class
namespace MyPluginRulesConditions;
use OrderDaemonCompletionManagerCoreRuleComponentsInterfacesConditionInterface;
class ShippingZoneCondition implements ConditionInterface {
public function get_id(): string {
return 'shipping_zone';
}
public function get_label(): string {
return __('Shipping Zone', 'my-plugin');
}
public function get_description(): string {
return __('Matches orders shipped to a specific WooCommerce shipping zone.', 'my-plugin');
}
public function get_settings_schema(): ?array {
// Build the list of zones for the dropdown
$zones = WC_Shipping_Zones::get_zones();
$options = [['value' => '0', 'label' => __('Rest of the World', 'my-plugin')]];
foreach ($zones as $zone) {
$options[] = [
'value' => (string) $zone['zone_id'],
'label' => $zone['zone_name'],
];
}
return [
'type' => 'object',
'properties' => [
'zone_id' => [
'type' => 'string',
'title' => __('Shipping Zone', 'my-plugin'),
'enum' => array_column($options, 'value'),
'enumNames' => array_column($options, 'label'),
'default' => '0',
],
],
'required' => ['zone_id'],
];
}
public function evaluate(WC_Order $order, array $settings): bool {
$target_zone_id = (int) ($settings['zone_id'] ?? 0);
$package = [
'destination' => [
'country' => $order->get_shipping_country(),
'state' => $order->get_shipping_state(),
'postcode' => $order->get_shipping_postcode(),
],
];
$zone = WC_Shipping_Zones::get_zone_matching_package($package);
return (int) $zone->get_id() === $target_zone_id;
}
}3. Register the component
Hook into odcm_register_conditions to add your component to the registry:
add_action('odcm_register_conditions', function ($registry) {
$registry->register_condition(new MyPluginRulesConditionsShippingZoneCondition());
});Place this in your plugin’s bootstrap (e.g. plugins_loaded or init).
4. Test it
- Go to Order Daemon → All Order Rules → Add New.
- Add a trigger (e.g. “Order Processing”).
- Add a condition – your new “Shipping Zone” condition should appear in the list.
- Configure it, save the rule, and place a test order to verify it fires correctly.
- Check Order Daemon → Insight Dashboard to see the condition’s pass/fail result in the timeline.
5. Add audit logging (optional but recommended)
Inside evaluate(), emit a log event for visibility:
public function evaluate(WC_Order $order, array $settings): bool {
$zone = WC_Shipping_Zones::get_zone_matching_package($package);
$matched = (int) $zone->get_id() === $target_zone_id;
if (function_exists('odcm_log_event')) {
odcm_log_event(
$matched
? 'Shipping zone condition passed'
: 'Shipping zone condition failed - zone mismatch',
[
'order_zone' => $zone->get_id(),
'target_zone' => $target_zone_id,
],
$order->get_id(),
$matched ? 'info' : 'debug',
'shipping_zone_condition'
);
}
return $matched;
}What’s next
- Full component interface reference: Creating custom rule components
- Defining richer settings UI: Settings schemas
- All hooks you can use: Hooks and filters reference
- Built-in component IDs: Built-in component catalog