Subscribe to Our Mailing List and Stay Up-to-Date!
Subscribe
extending+nexus+pro
Developer Guides

Extending Nexus Pro: Add Custom Fields to Entity Templates

Nexus Pro provides Person, Product, and Place entity templates with essential schema fields out of the box. However, many projects require additional custom fields—industry-specific data, extra properties, or specialized metadata. Extending Nexus Pro through hooks and filters allows adding custom fields while maintaining plugin compatibility and update safety.

extending+nexus+pro

This developer guide shows you how to extend Nexus Pro’s entity templates, add custom meta fields, integrate with schema output, and create admin interfaces for your custom data.

Understanding Nexus Pro Architecture

Foundation for safe extensions.

Entity Template Structure

Three Custom Post Types:

1. Person (nexus_person):

  • Name, job title, bio
  • Social profiles
  • Contact information
  • Person schema markup

2. Product (nexus_product):

  • Product name, description
  • Price, availability
  • Images, SKU
  • Product schema markup

3. Place (nexus_place):

  • Business name, type
  • Address, coordinates
  • Hours, contact
  • LocalBusiness schema markup

Hook System

Nexus Pro provides hooks for:

  • Adding meta fields to admin
  • Saving custom meta data
  • Filtering schema output
  • Modifying entity queries
  • Extending admin interfaces

Hook Naming Convention:

nexus_pro_{entity}_{action}

Examples:

  • nexus_pro_person_meta_fields
  • nexus_pro_product_schema
  • nexus_pro_place_save_meta

Adding Custom Fields to Person Entity

Extend Person template with additional fields.

Register Custom Meta Fields

Add Fields to Admin:

/**
 * Add custom fields to Person entity edit screen.
 */
function add_custom_person_fields($post) {
    // Get current values
    $linkedin = get_post_meta($post->ID, 'linkedin_url', true);
    $github = get_post_meta($post->ID, 'github_url', true);
    $expertise = get_post_meta($post->ID, 'expertise_areas', true);
    ?>
    <div class="nexus-pro-custom-fields">
        <h3>Additional Professional Information</h3>

        <p>
            <label for="linkedin_url">LinkedIn Profile:</label><br>
            <input type="url" id="linkedin_url" name="linkedin_url"
                   value="<?php echo esc_attr($linkedin); ?>"
                   class="widefat" placeholder="https://linkedin.com/in/username">
        </p>

        <p>
            <label for="github_url">GitHub Profile:</label><br>
            <input type="url" id="github_url" name="github_url"
                   value="<?php echo esc_attr($github); ?>"
                   class="widefat" placeholder="https://github.com/username">
        </p>

        <p>
            <label for="expertise_areas">Expertise Areas (comma-separated):</label><br>
            <input type="text" id="expertise_areas" name="expertise_areas"
                   value="<?php echo esc_attr($expertise); ?>"
                   class="widefat" placeholder="WordPress, PHP, JavaScript">
        </p>
    </div>
    <?php
}
add_action('nexus_pro_person_meta_fields', 'add_custom_person_fields');

Save Custom Meta Data

Hook Into Save Process:

/**
 * Save custom Person meta fields.
 */
function save_custom_person_meta($post_id) {
    // Security checks
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    if (!current_user_can('edit_post', $post_id)) {
        return;
    }

    // Verify post type
    if (get_post_type($post_id) !== 'nexus_person') {
        return;
    }

    // Save LinkedIn URL
    if (isset($_POST['linkedin_url'])) {
        update_post_meta(
            $post_id,
            'linkedin_url',
            esc_url_raw($_POST['linkedin_url'])
        );
    }

    // Save GitHub URL
    if (isset($_POST['github_url'])) {
        update_post_meta(
            $post_id,
            'github_url',
            esc_url_raw($_POST['github_url'])
        );
    }

    // Save expertise areas
    if (isset($_POST['expertise_areas'])) {
        $expertise = sanitize_text_field($_POST['expertise_areas']);
        update_post_meta($post_id, 'expertise_areas', $expertise);
    }
}
add_action('save_post_nexus_person', 'save_custom_person_meta');

Add to Person Schema

Extend Schema Output:

/**
 * Add custom fields to Person schema.
 */
function extend_person_schema($schema, $post_id) {
    // Add LinkedIn to sameAs
    $linkedin = get_post_meta($post_id, 'linkedin_url', true);
    if ($linkedin) {
        if (!isset($schema['sameAs'])) {
            $schema['sameAs'] = [];
        }
        $schema['sameAs'][] = $linkedin;
    }

    // Add GitHub to sameAs
    $github = get_post_meta($post_id, 'github_url', true);
    if ($github) {
        if (!isset($schema['sameAs'])) {
            $schema['sameAs'] = [];
        }
        $schema['sameAs'][] = $github;
    }

    // Add expertise as knowsAbout
    $expertise = get_post_meta($post_id, 'expertise_areas', true);
    if ($expertise) {
        $areas = array_map('trim', explode(',', $expertise));
        $schema['knowsAbout'] = $areas;
    }

    return $schema;
}
add_filter('nexus_pro_person_schema', 'extend_person_schema', 10, 2);

Adding Custom Fields to Product Entity

Extend Product template.

Product Meta Fields

Additional Product Data:

/**
 * Add custom fields to Product entity.
 */
function add_custom_product_fields($post) {
    $dimensions = get_post_meta($post->ID, 'product_dimensions', true);
    $weight = get_post_meta($post->ID, 'product_weight', true);
    $material = get_post_meta($post->ID, 'product_material', true);
    $warranty = get_post_meta($post->ID, 'warranty_period', true);
    ?>
    <div class="nexus-pro-product-specs">
        <h3>Product Specifications</h3>

        <p>
            <label for="product_dimensions">Dimensions (L x W x H in cm):</label><br>
            <input type="text" id="product_dimensions" name="product_dimensions"
                   value="<?php echo esc_attr($dimensions); ?>"
                   placeholder="30 x 20 x 15">
        </p>

        <p>
            <label for="product_weight">Weight (kg):</label><br>
            <input type="number" id="product_weight" name="product_weight"
                   value="<?php echo esc_attr($weight); ?>"
                   step="0.01" min="0">
        </p>

        <p>
            <label for="product_material">Material:</label><br>
            <input type="text" id="product_material" name="product_material"
                   value="<?php echo esc_attr($material); ?>"
                   placeholder="Stainless Steel">
        </p>

        <p>
            <label for="warranty_period">Warranty Period (months):</label><br>
            <input type="number" id="warranty_period" name="warranty_period"
                   value="<?php echo esc_attr($warranty); ?>"
                   min="0">
        </p>
    </div>
    <?php
}
add_action('nexus_pro_product_meta_fields', 'add_custom_product_fields');

Save Product Meta

/**
 * Save custom Product meta fields.
 */
function save_custom_product_meta($post_id) {
    if (get_post_type($post_id) !== 'nexus_product') {
        return;
    }

    $fields = [
        'product_dimensions' => 'sanitize_text_field',
        'product_weight' => 'floatval',
        'product_material' => 'sanitize_text_field',
        'warranty_period' => 'absint'
    ];

    foreach ($fields as $field => $sanitize_callback) {
        if (isset($_POST[$field])) {
            $value = call_user_func($sanitize_callback, $_POST[$field]);
            update_post_meta($post_id, $field, $value);
        }
    }
}
add_action('save_post_nexus_product', 'save_custom_product_meta');

Extend Product Schema

/**
 * Add specifications to Product schema.
 */
function extend_product_schema($schema, $post_id) {
    // Add dimensions
    $dimensions = get_post_meta($post_id, 'product_dimensions', true);
    if ($dimensions) {
        $schema['depth'] = [
            '@type' => 'QuantitativeValue',
            'value' => $dimensions
        ];
    }

    // Add weight
    $weight = get_post_meta($post_id, 'product_weight', true);
    if ($weight) {
        $schema['weight'] = [
            '@type' => 'QuantitativeValue',
            'value' => $weight,
            'unitCode' => 'KGM' // Kilograms
        ];
    }

    // Add material
    $material = get_post_meta($post_id, 'product_material', true);
    if ($material) {
        $schema['material'] = $material;
    }

    // Add warranty
    $warranty = get_post_meta($post_id, 'warranty_period', true);
    if ($warranty) {
        $schema['warranty'] = [
            '@type' => 'WarrantyPromise',
            'durationOfWarranty' => [
                '@type' => 'QuantitativeValue',
                'value' => $warranty,
                'unitCode' => 'MON' // Months
            ]
        ];
    }

    return $schema;
}
add_filter('nexus_pro_product_schema', 'extend_product_schema', 10, 2);

Adding Custom Fields to Place Entity

Extend LocalBusiness template.

Place Meta Fields

Business-Specific Data:

/**
 * Add custom fields to Place entity.
 */
function add_custom_place_fields($post) {
    $parking = get_post_meta($post->ID, 'parking_available', true);
    $accessibility = get_post_meta($post->ID, 'wheelchair_accessible', true);
    $payment_methods = get_post_meta($post->ID, 'payment_methods', true);
    $languages = get_post_meta($post->ID, 'languages_spoken', true);
    ?>
    <div class="nexus-pro-place-amenities">
        <h3>Business Amenities</h3>

        <p>
            <label>
                <input type="checkbox" name="parking_available" value="1"
                       <?php checked($parking, '1'); ?>>
                Parking Available
            </label>
        </p>

        <p>
            <label>
                <input type="checkbox" name="wheelchair_accessible" value="1"
                       <?php checked($accessibility, '1'); ?>>
                Wheelchair Accessible
            </label>
        </p>

        <p>
            <label>Payment Methods Accepted:</label><br>
            <select name="payment_methods[]" multiple size="5" class="widefat">
                <?php
                $methods = ['Cash', 'Credit Card', 'Debit Card', 'PayPal', 'Apple Pay', 'Google Pay'];
                $selected = is_array($payment_methods) ? $payment_methods : [];
                foreach ($methods as $method) {
                    $is_selected = in_array($method, $selected) ? 'selected' : '';
                    echo "<option value='" . esc_attr($method) . "' $is_selected>$method</option>";
                }
                ?>
            </select>
            <small>Hold Ctrl/Cmd to select multiple</small>
        </p>

        <p>
            <label>Languages Spoken:</label><br>
            <input type="text" name="languages_spoken"
                   value="<?php echo esc_attr($languages); ?>"
                   class="widefat" placeholder="English, Spanish, French">
        </p>
    </div>
    <?php
}
add_action('nexus_pro_place_meta_fields', 'add_custom_place_fields');

Save Place Meta

/**
 * Save custom Place meta fields.
 */
function save_custom_place_meta($post_id) {
    if (get_post_type($post_id) !== 'nexus_place') {
        return;
    }

    // Save checkboxes
    update_post_meta($post_id, 'parking_available',
        isset($_POST['parking_available']) ? '1' : '0');

    update_post_meta($post_id, 'wheelchair_accessible',
        isset($_POST['wheelchair_accessible']) ? '1' : '0');

    // Save payment methods array
    if (isset($_POST['payment_methods'])) {
        $methods = array_map('sanitize_text_field', $_POST['payment_methods']);
        update_post_meta($post_id, 'payment_methods', $methods);
    } else {
        delete_post_meta($post_id, 'payment_methods');
    }

    // Save languages
    if (isset($_POST['languages_spoken'])) {
        update_post_meta($post_id, 'languages_spoken',
            sanitize_text_field($_POST['languages_spoken']));
    }
}
add_action('save_post_nexus_place', 'save_custom_place_meta');

Extend LocalBusiness Schema

/**
 * Add amenities to LocalBusiness schema.
 */
function extend_place_schema($schema, $post_id) {
    // Add parking
    $parking = get_post_meta($post_id, 'parking_available', true);
    if ($parking === '1') {
        $schema['amenityFeature'][] = [
            '@type' => 'LocationFeatureSpecification',
            'name' => 'Parking',
            'value' => true
        ];
    }

    // Add accessibility
    $accessibility = get_post_meta($post_id, 'wheelchair_accessible', true);
    if ($accessibility === '1') {
        $schema['amenityFeature'][] = [
            '@type' => 'LocationFeatureSpecification',
            'name' => 'Wheelchair Accessible',
            'value' => true
        ];
    }

    // Add payment methods
    $payment_methods = get_post_meta($post_id, 'payment_methods', true);
    if (is_array($payment_methods) && !empty($payment_methods)) {
        $schema['paymentAccepted'] = implode(', ', $payment_methods);
    }

    // Add languages
    $languages = get_post_meta($post_id, 'languages_spoken', true);
    if ($languages) {
        $lang_array = array_map('trim', explode(',', $languages));
        $schema['availableLanguage'] = $lang_array;
    }

    return $schema;
}
add_filter('nexus_pro_place_schema', 'extend_place_schema', 10, 2);

Creating Custom Meta Boxes

Professional admin interfaces.

Meta Box with Tabs

Organized Field Groups:

/**
 * Register tabbed meta box.
 */
function register_tabbed_meta_box() {
    add_meta_box(
        'nexus_pro_extended_meta',
        'Extended Information',
        'render_tabbed_meta_box',
        ['nexus_person', 'nexus_product', 'nexus_place'],
        'normal',
        'high'
    );
}
add_action('add_meta_boxes', 'register_tabbed_meta_box');

/**
 * Render tabbed interface.
 */
function render_tabbed_meta_box($post) {
    wp_nonce_field('save_extended_meta', 'extended_meta_nonce');
    ?>
    <div class="nexus-pro-tabs">
        <ul class="tab-nav">
            <li><a href="#tab-basic" class="active">Basic Info</a></li>
            <li><a href="#tab-advanced">Advanced</a></li>
            <li><a href="#tab-schema">Schema Data</a></li>
        </ul>

        <div id="tab-basic" class="tab-content active">
            <?php render_basic_fields($post); ?>
        </div>

        <div id="tab-advanced" class="tab-content">
            <?php render_advanced_fields($post); ?>
        </div>

        <div id="tab-schema" class="tab-content">
            <?php render_schema_fields($post); ?>
        </div>
    </div>

    <style>
        .tab-nav { list-style: none; margin: 0; padding: 0; border-bottom: 1px solid #ccc; }
        .tab-nav li { display: inline-block; margin: 0; }
        .tab-nav a { display: block; padding: 10px 15px; text-decoration: none; border: 1px solid #ccc; border-bottom: none; background: #f1f1f1; }
        .tab-nav a.active { background: #fff; }
        .tab-content { display: none; padding: 20px; border: 1px solid #ccc; border-top: none; }
        .tab-content.active { display: block; }
    </style>

    <script>
    jQuery(document).ready(function($) {
        $('.tab-nav a').click(function(e) {
            e.preventDefault();
            $('.tab-nav a, .tab-content').removeClass('active');
            $(this).addClass('active');
            $($(this).attr('href')).addClass('active');
        });
    });
    </script>
    <?php
}

Repeatable Fields

Dynamic Field Groups:

/**
 * Render repeatable fields (e.g., certifications).
 */
function render_repeatable_certifications($post) {
    $certifications = get_post_meta($post->ID, 'certifications', true);
    if (!is_array($certifications)) {
        $certifications = [];
    }
    ?>
    <div id="certifications-container">
        <h4>Certifications</h4>
        <div class="certifications-list">
            <?php
            if (empty($certifications)) {
                $certifications = [['name' => '', 'issuer' => '', 'date' => '']];
            }
            foreach ($certifications as $index => $cert) {
                ?>
                <div class="certification-item" data-index="<?php echo $index; ?>">
                    <input type="text" name="certifications[<?php echo $index; ?>][name]"
                           value="<?php echo esc_attr($cert['name'] ?? ''); ?>"
                           placeholder="Certification Name">
                    <input type="text" name="certifications[<?php echo $index; ?>][issuer]"
                           value="<?php echo esc_attr($cert['issuer'] ?? ''); ?>"
                           placeholder="Issuing Organization">
                    <input type="date" name="certifications[<?php echo $index; ?>][date]"
                           value="<?php echo esc_attr($cert['date'] ?? ''); ?>">
                    <button type="button" class="remove-certification">Remove</button>
                </div>
                <?php
            }
            ?>
        </div>
        <button type="button" id="add-certification">Add Certification</button>
    </div>

    <script>
    jQuery(document).ready(function($) {
        var certIndex = <?php echo count($certifications); ?>;

        $('#add-certification').click(function() {
            var html = '<div class="certification-item" data-index="' + certIndex + '">' +
                '<input type="text" name="certifications[' + certIndex + '][name]" placeholder="Certification Name">' +
                '<input type="text" name="certifications[' + certIndex + '][issuer]" placeholder="Issuing Organization">' +
                '<input type="date" name="certifications[' + certIndex + '][date]">' +
                '<button type="button" class="remove-certification">Remove</button>' +
                '</div>';
            $('.certifications-list').append(html);
            certIndex++;
        });

        $(document).on('click', '.remove-certification', function() {
            $(this).closest('.certification-item').remove();
        });
    });
    </script>
    <?php
}

Best Practices

Professional extension guidelines.

Use Proper Hook Priorities

// Default priority (10)
add_filter('nexus_pro_person_schema', 'extend_person_schema', 10, 2);

// Run late to ensure all other modifications are done
add_filter('nexus_pro_person_schema', 'final_person_adjustments', 999, 2);

Validate and Sanitize

// Always validate and sanitize user input
function save_custom_meta($post_id) {
    // URL validation
    if (isset($_POST['website'])) {
        $url = esc_url_raw($_POST['website']);
        if (filter_var($url, FILTER_VALIDATE_URL)) {
            update_post_meta($post_id, 'website', $url);
        }
    }

    // Email validation
    if (isset($_POST['email'])) {
        $email = sanitize_email($_POST['email']);
        if (is_email($email)) {
            update_post_meta($post_id, 'email', $email);
        }
    }

    // Number validation
    if (isset($_POST['price'])) {
        $price = floatval($_POST['price']);
        if ($price >= 0) {
            update_post_meta($post_id, 'price', $price);
        }
    }
}

Check for Empty Values

// Don't add empty fields to schema
function extend_schema_safely($schema, $post_id) {
    $custom_field = get_post_meta($post_id, 'custom_field', true);

    if (!empty($custom_field)) {
        $schema['customProperty'] = $custom_field;
    }

    return $schema;
}

Use Nonces for Security

// Add nonce to form
wp_nonce_field('save_custom_meta', 'custom_meta_nonce');

// Verify nonce when saving
function save_custom_meta($post_id) {
    if (!isset($_POST['custom_meta_nonce']) ||
        !wp_verify_nonce($_POST['custom_meta_nonce'], 'save_custom_meta')) {
        return;
    }

    // Proceed with saving
}

Document Your Extensions

/**
 * Extend Nexus Pro Person entity with professional certifications.
 *
 * Adds repeatable certification fields to Person entity admin interface
 * and includes them in Person schema output as credentials.
 *
 * @since 1.0.0
 * @param WP_Post $post The post object.
 */
function add_certification_fields($post) {
    // Implementation
}

Conclusion

Extending Nexus Pro through hooks and filters allows adding custom fields to entity templates while maintaining plugin compatibility. By understanding the hook system, implementing proper validation, and following WordPress development best practices, you can customize Nexus Pro for any project requirements.

Extension Checklist:

  1. ✓ Use Nexus Pro hooks (not core files)
  2. ✓ Add meta fields with action hooks
  3. ✓ Save data securely with nonces
  4. ✓ Validate and sanitize all input
  5. ✓ Extend schema with filter hooks
  6. ✓ Check for empty values
  7. ✓ Test schema with validators
  8. ✓ Document custom code
  9. ✓ Prefix function names
  10. ✓ Test with plugin updates

Available Hooks:

  • nexus_pro_{entity}_meta_fields – Add fields to admin
  • nexus_pro_{entity}_schema – Filter schema output
  • save_post_nexus_{entity} – Save custom meta
  • nexus_pro_{entity}_query – Modify entity queries

Start with simple single-field additions to understand the hook system, then progress to complex meta boxes, repeatable fields, and advanced schema integrations. Always test extensions after Nexus Pro updates to ensure compatibility.


Related Articles:

Leave a Reply

Your email address will not be published. Required fields are marked *