Extending
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.
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 |
|---|---|---|---|
type | string | 'text' | Field type (text, textarea, select, checkbox, radio, currency, gallery, etc.) |
label | string | — | Human-readable label displayed in forms and admin |
description | string | '' | Help text shown below the field |
required | bool | false | Whether the field must be filled in |
default | mixed | '' | Default value for new listings |
placeholder | string | '' | Placeholder text for text inputs |
options | array | [] | Key-value pairs for select, radio, and checkbox fields |
validation | array | [] | Custom validation rules: min_length, max_length, pattern, callback |
searchable | bool | false | Include this field in search queries |
filterable | bool | false | Show this field as a search filter |
sortable | bool | false | Allow sorting listings by this field |
admin_only | bool | false | Hide from the frontend submission form |
priority | int | 10 | Display order (lower numbers appear first) |
class | string | '' | CSS classes added to the field wrapper |
attributes | array | [] | Extra HTML attributes added to the input element |
'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.
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:
getType()— Returns the unique type identifier used in field configuration.render()— Returns the HTML markup for the form input.sanitize()— Cleans user input before saving to the database.validate()— Returnstrueon success or aWP_Erroron failure.supports()— Declares which features the field type supports (e.g.,searchable,sortable).formatValue()— Formats the stored value for frontend display.
Search
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.
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 |
|---|---|
keyword | Free-text search input |
category | Taxonomy category dropdown selection |
tag | Taxonomy tag selection |
range | Min/max numeric range inputs |
date_range | Start 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.
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:
get_name()— A unique slug used in shortcode attributes (e.g.,[apd_listings view="map"]).get_label()— Human-readable label shown in the view switcher.get_icon()— Dashicon class for the view toggle button.render()— Returns the complete HTML for the listings display.supports()— Declares supported features such asajaxfor live filtering.
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:
- Child theme:
wp-content/themes/child-theme/damdir-directory/ - Parent theme:
wp-content/themes/parent-theme/damdir-directory/ - 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:
// 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:
// 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. |