Skip to main content
FormConfigManager lives in UTB\ProductBuilder\Data and is the single entry point for retrieving the field configuration that drives a flow’s front-end form. It follows a three-step resolution chain.
use UTB\ProductBuilder\Data\FormConfigManager;

$manager = new FormConfigManager();
$config  = $manager->get_config($product_id, $flow_id);

Methods

get_config(int $product_id, string $flow_id): ?array

Returns the resolved form configuration for a product, or null if no configuration can be found.
product_id
integer
required
The WooCommerce product ID.
flow_id
string
required
The flow’s machine identifier (e.g. certificados_academicos).
public function get_config(int $product_id, string $flow_id): ?array
Returns an array with the following shape, or null:
{
  "id": 42,
  "custom_css": ".utb-form { font-size: 14px; }",
  "fields": [ /* array of field definition objects */ ]
}

Resolution chain

get_config() resolves the configuration in this order:
1

Product-level config (Hub way)

Reads the _utb_form_config post meta key on the product. If it contains valid JSON, returns it immediately with id set to $product_id.
2

Linked certificate (legacy / template)

Reads _utb_linked_cert_id post meta. Falls back to $_GET['cert_id'] for testing. Fetches the certificate row from the database and decodes its form_config_json column.
3

Flow default config

Calls FlowRegistry::get($flow_id) and invokes get_default_config() on the returned flow object if the method exists.
If none of the three steps yield a valid configuration, the method returns null.

Storing a product-level config

To attach a form configuration directly to a product, encode the fields array as JSON and save it to the _utb_form_config meta key:
$fields = [
    [
        'id'       => 'student_name',
        'type'     => 'text',
        'label'    => 'Full name',
        'required' => true,
    ],
    [
        'id'      => 'program',
        'type'    => 'select',
        'label'   => 'Program',
        'source'  => 'cep_programs', // data source key
    ],
];

update_post_meta($product_id, '_utb_form_config', wp_json_encode($fields));

// Optional per-product CSS:
update_post_meta($product_id, '_utb_custom_css', '.utb-form input { border-radius: 4px; }');

Field object schema

The fields array contains field definition objects. The exact shape depends on the field type, but all fields share these base properties:
id
string
required
Unique field identifier within the form. Becomes the key in form_submission on the order.
type
string
required
Field type. Common values: text, email, tel, select, radio, checkbox, file, hidden, textarea.
label
string
Human-readable label shown above the field.
required
boolean
Whether the field must be filled before adding to cart.
source
string
Data source key for dynamic fields (select, radio). The flow uses this key to populate options from the configured data repository.
conditional
object
Conditional visibility rule. When defined, the field is shown or hidden based on the value of another field.
validation
object
Additional validation rules evaluated by the RulesEngine (available since v3.0.0).

Config object returned

id
integer | string
The product ID, linked certificate ID, or 'default_flow_config' depending on which resolution step succeeded.
custom_css
string
Raw CSS injected into the product page for this form. May be empty.
fields
object[]
Array of field definition objects (see Field object schema above).

Usage in a flow

use UTB\ProductBuilder\Data\FormConfigManager;

protected function render_custom_content(): void
{
    $product_id = get_queried_object_id();
    $manager    = new FormConfigManager();
    $config     = $manager->get_config($product_id, $this->get_id());

    if (!$config) {
        echo '<p>No form configuration found for this product.</p>';
        return;
    }

    if (!empty($config['custom_css'])) {
        echo '<style>' . wp_strip_all_tags($config['custom_css']) . '</style>';
    }

    foreach ($config['fields'] as $field) {
        // render each field...
    }
}

Build docs developers (and LLMs) love