← Developer Reference

Extending

This reference targets APD v1.0.x. Last updated: 2026-02-21.

How to add custom field types, search filters, display views, and template overrides to DamDir Directory.

Custom Fields

Registering Fields

Use the apd_listing_fields filter to register custom fields for your listings. Each field is defined as an associative array keyed by the field name.

PHP
add_filter( 'apd_listing_fields', function( $fields ) {
    // Simple text field
    $fields['business_hours'] = [
        'type'        => 'textarea',
        'label'       => 'Business Hours',
        'description' => 'Enter your operating hours',
        'required'    => false,
        'rows'        => 4,
    ];

    // Select field with options
    $fields['property_type'] = [
        'type'        => 'select',
        'label'       => 'Property Type',
        'required'    => true,
        'options'     => [
            'house'     => 'House',
            'apartment' => 'Apartment',
            'condo'     => 'Condo',
            'land'      => 'Land',
        ],
        'filterable'  => true,
    ];

    // Price field with currency
    $fields['price'] = [
        'type'            => 'currency',
        'label'           => 'Price',
        'required'        => true,
        'min'             => 0,
        'currency_symbol' => '$',
        'filterable'      => true,
        'sortable'        => true,
    ];

    // Gallery field
    $fields['gallery'] = [
        'type'       => 'gallery',
        'label'      => 'Photo Gallery',
        'max_images' => 10,
    ];

    return $fields;
});

Field Options

Every field accepts the following configuration options. Only type and label are required; all other options have sensible defaults.

Option Type Default Description
typestring'text'Field type (text, textarea, select, checkbox, radio, currency, gallery, etc.)
labelstringHuman-readable label displayed in forms and admin
descriptionstring''Help text shown below the field
requiredboolfalseWhether the field must be filled in
defaultmixed''Default value for new listings
placeholderstring''Placeholder text for text inputs
optionsarray[]Key-value pairs for select, radio, and checkbox fields
validationarray[]Custom validation rules: min_length, max_length, pattern, callback
searchableboolfalseInclude this field in search queries
filterableboolfalseShow this field as a search filter
sortableboolfalseAllow sorting listings by this field
admin_onlyboolfalseHide from the frontend submission form
priorityint10Display order (lower numbers appear first)
classstring''CSS classes added to the field wrapper
attributesarray[]Extra HTML attributes added to the input element
PHP — Validation example
'validation' => [
    'min_length' => 10,
    'max_length' => 500,
    'pattern'    => '/^[A-Z]/',     // Regex
    'callback'   => 'my_validator', // Custom function
],

Custom Field Types

If the built-in field types don't fit your needs, implement FieldTypeInterface or extend AbstractFieldType. Your class needs to define how the field renders, sanitizes input, validates values, declares supported features, and formats output.

PHP
use APD\Contracts\FieldTypeInterface;
use APD\Fields\AbstractFieldType;

class CustomFieldType extends AbstractFieldType {
    public function getType(): string {
        return 'custom';
    }

    public function render( array $field, mixed $value ): string {
        return sprintf(
            '<input type="text" name="%s" value="%s" class="custom-input" />',
            esc_attr( $field['name'] ),
            esc_attr( $value )
        );
    }

    public function sanitize( mixed $value ): mixed {
        return sanitize_text_field( $value );
    }

    public function validate( mixed $value, array $field ): bool|WP_Error {
        if ( $field['required'] && empty( $value ) ) {
            return new WP_Error( 'required', 'This field is required.' );
        }
        return true;
    }

    public function supports( string $feature ): bool {
        return in_array( $feature, [ 'searchable', 'sortable' ], true );
    }

    public function formatValue( mixed $value, array $field ): string {
        return esc_html( $value );
    }
}

// Register the field type
add_action( 'apd_init', function() {
    apd_register_field_type( new CustomFieldType() );
});

The key methods:

Custom Filters

Register custom search filters using the apd_search_filters filter. Each filter defines its type, the field it operates on, and a query_callback that modifies the WordPress query arguments.

PHP
add_filter( 'apd_search_filters', function( $filters ) {
    $filters['price_range'] = [
        'type'           => 'range',
        'label'          => 'Price Range',
        'field'          => 'price',
        'min'            => 0,
        'max'            => 1000000,
        'step'           => 1000,
        'query_callback' => function( $query_args, $value ) {
            if ( ! empty( $value['min'] ) ) {
                $query_args['meta_query'][] = [
                    'key'     => '_apd_price',
                    'value'   => $value['min'],
                    'compare' => '>=',
                    'type'    => 'NUMERIC',
                ];
            }
            if ( ! empty( $value['max'] ) ) {
                $query_args['meta_query'][] = [
                    'key'     => '_apd_price',
                    'value'   => $value['max'],
                    'compare' => '<=',
                    'type'    => 'NUMERIC',
                ];
            }
            return $query_args;
        },
    ];

    return $filters;
});

Filter Types

APD ships with five built-in filter types:

Type Description
keywordFree-text search input
categoryTaxonomy category dropdown selection
tagTaxonomy tag selection
rangeMin/max numeric range inputs
date_rangeStart and end date picker inputs

Display

Custom Views

Create custom display views by implementing ViewInterface or extending AbstractView. A view controls how a collection of listings is rendered on the frontend.

PHP
use APD\Contracts\ViewInterface;
use APD\Frontend\Display\AbstractView;

class MapView extends AbstractView {
    public function get_name(): string {
        return 'map';
    }

    public function get_label(): string {
        return __( 'Map View', 'my-plugin' );
    }

    public function get_icon(): string {
        return 'dashicons-location';
    }

    public function render( array $listings, array $args = [] ): string {
        ob_start();
        // Render map with listings
        echo '<div class="apd-map-view" data-listings="'
            . esc_attr( json_encode( $this->get_map_data( $listings ) ) )
            . '"></div>';
        return ob_get_clean();
    }

    public function supports( string $feature ): bool {
        return in_array( $feature, [ 'ajax' ], true );
    }
}

// Register the view
add_action( 'apd_views_init', function() {
    apd_register_view( new MapView() );
});

The key methods:

Templates

Template System

DamDir Directory uses a template system that lets theme developers override any plugin template. Templates are plain PHP files that receive data as extracted variables.

Template Hierarchy

When APD loads a template, it checks these locations in order and uses the first match:

  1. Child theme: wp-content/themes/child-theme/damdir-directory/
  2. Parent theme: wp-content/themes/parent-theme/damdir-directory/
  3. Plugin: wp-content/plugins/damdir-directory/templates/

To override a template, copy it from the plugin's templates/ directory into your theme under an damdir-directory/ folder, keeping the same filename and subdirectory structure. Always use a child theme so your overrides survive theme updates.

Using Templates

APD provides four helper functions for working with templates:

PHP
// Render a template with data
apd_get_template( 'listing-card.php', [
    'listing' => $post,
    'view'    => 'grid',
] );

// Get template output as a string
$html = apd_get_template_html( 'listing-card.php', [ 'listing' => $post ] );

// Check if a template exists
if ( apd_template_exists( 'custom-template.php' ) ) {
    apd_get_template( 'custom-template.php' );
}

// Check if a theme overrides a plugin template
if ( apd_is_template_overridden( 'single-listing.php' ) ) {
    // Theme has a custom template
}

Data you pass as the second argument to apd_get_template() is extracted into local variables inside the template file:

PHP — Inside listing-card.php
// In your code
apd_get_template( 'listing-card.php', [
    'listing'     => $post,
    'show_image'  => true,
    'custom_data' => 'value',
] );

// In listing-card.php template
echo $listing->post_title;
if ( $show_image ) {
    echo get_the_post_thumbnail( $listing->ID );
}
echo $custom_data;
Function Description
apd_get_template()Locates and includes a template file, extracting the passed data array into local variables.
apd_get_template_html()Same as above but captures and returns the output as a string instead of echoing it.
apd_template_exists()Returns true if the given template file can be found in the template hierarchy.
apd_is_template_overridden()Returns true if a theme provides its own version of the template.