Docs Developer Reference

Developer Reference

Architecture, hooks, REST API, database schema, and everything you need to extend Local Directory.

This page is the technical reference for developers who want to extend, customize, or integrate with Local Directory. It covers the plugin architecture, public API surface, database schema, hook system, and asset pipeline.

Architecture

Local Directory uses a singleton + PSR-4-style autoloader pattern. The main plugin file local-directory.php defines the ld_plugin() function, which returns the single instance of LD_Plugin. All subsystems are instantiated during the plugins_loaded hook.

Bootstrap Flow

local-directory.php  →  ld_plugin()  →  LD_Plugin::instance()

Inside LD_Plugin::instance(), the following subsystems are created:

new LD_Admin();
new LD_Shortcodes();
new LD_Badges();
new LD_Favorites();
new LD_Claim_Listing();
new LD_Report();
new LD_Email_Manager();
new LD_Schema();
new LD_Open_Graph();
new LD_Analytics_Tracker();
new LD_Search_Query();
new LD_Compare_Controller();
new LD_Blocks();

Autoloader

Classes follow the class-ld-{name}.php naming convention. The autoloader resolves classes from these directories:

  • includes/
  • includes/admin/
  • includes/frontend/
  • includes/models/
  • includes/features/
  • includes/search/
  • includes/maps/
  • includes/seo/
  • includes/blocks/
  • includes/elementor/
  • includes/elementor/widgets/

Controller Pattern

Shortcode controllers follow a consistent render pipeline:

render($atts)  →  get_atts()  →  WP_Query  →  LD_Template_Loader::load()

Each controller normalizes shortcode attributes via get_atts(), builds a WP_Query for listing data, and passes results to the template loader which supports child-theme overrides.

Constants

These constants are defined in local-directory.php and available globally after the plugin loads.

Constant Value Description
LD_VERSION 1.0.5 Current plugin version string.
LD_PLUGIN_FILE __FILE__ Absolute path to the main plugin file.
LD_PLUGIN_DIR plugin_dir_path() Absolute filesystem path to the plugin directory (with trailing slash).
LD_PLUGIN_URL plugin_dir_url() URL to the plugin directory (with trailing slash).
LD_PLUGIN_BASENAME plugin_basename() Relative path from wp-content/plugins/ (e.g., local-directory/local-directory.php).

Custom Post Type

The plugin registers the ld_listing custom post type during init.

Property Value
Post type slug ld_listing
Supports title, editor, thumbnail, excerpt, comments, author
REST API Enabled (show_in_rest = true)
Archive Enabled
Rewrite slug listing
Menu icon dashicons-location

Querying Listings

$listings = new WP_Query([
    'post_type'      => 'ld_listing',
    'posts_per_page' => 10,
    'orderby'        => 'date',
    'order'          => 'DESC',
]);

Taxonomies

Three custom taxonomies are registered for the ld_listing post type.

Taxonomy Hierarchical Rewrite Slug Description
ld_category Yes listing-category Business categories (e.g., Restaurants, Hotels, Shopping). Supports icons and custom fields per category.
ld_location Yes listing-location Geographic locations with parent-child hierarchy (Country → State/Region → City).
ld_tag No listing-tag Free-form tags for listings (e.g., "pet-friendly", "free-wifi", "outdoor-seating").

Database Tables

Local Directory creates 9 custom tables on activation. All table names are prefixed with $wpdb->prefix (typically wp_).

Table Description
{prefix}ld_field_groups Custom field group definitions (tab/section labels, sort order).
{prefix}ld_fields Custom field definitions (type, label, options, visibility, validation rules).
{prefix}ld_locations_data Listing-to-location mapping with coordinates (lat/lng), formatted address, and Google Place ID.
{prefix}ld_rating_criteria Rating criteria definitions per category (e.g., Food, Service, Ambiance).
{prefix}ld_ratings_summary Aggregated rating scores per listing per criterion (count, sum, average).
{prefix}ld_import_log Import history with source type, record count, mapping used, and Google Place IDs for deduplication.
{prefix}ld_claims Ownership claims with verification code, status (pending/approved/rejected), and claimant data.
{prefix}ld_reports User-submitted reports for listings (spam, incorrect info, closed, etc.).
{prefix}ld_analytics Event tracking data (views, clicks, searches, favorites) with timestamps and session info.

Note: Tables are created using dbDelta() during activation. The plugin checks table versions and runs migrations automatically on update.

Key Models

Models wrap database records and provide a clean API for reading and writing data. All models are in includes/models/.

LD_Listing

Wraps a WP_Post of type ld_listing. The primary model used throughout the plugin for listing display, search results, and data export.

// All public getters on LD_Listing

$listing->get_id()
$listing->get_title()
$listing->get_permalink()
$listing->get_phone()
$listing->get_website()
$listing->get_opening_hours()
$listing->get_location_data()
$listing->get_lat()
$listing->get_lng()
$listing->get_address()
$listing->get_overall_rating()
$listing->get_criteria_ratings()
$listing->get_price_level()
$listing->get_badges()
$listing->is_featured()
$listing->is_verified()
$listing->is_new()
$listing->to_marker_data()
$listing->to_card_data()
$listing->to_compare_data()

LD_Location_Data

Manages the ld_locations_data table. Stores latitude, longitude, formatted address, and Google Place ID per listing.

// Static methods
LD_Location_Data::get_by_listing( $listing_id )  // Returns location row or null
LD_Location_Data::save( $listing_id, $data )      // Upserts location data

LD_Claim

Manages ownership claims in the ld_claims table.

LD_Claim::create( $listing_id, $user_data )
LD_Claim::get( $claim_id )
LD_Claim::verify_code( $claim_id, $code )
LD_Claim::approve( $claim_id )
LD_Claim::reject( $claim_id )
LD_Claim::get_approved_for_listing( $listing_id )

LD_Review

Handles multi-criteria ratings linked to WordPress comments.

LD_Review::get_criteria_for_listing( $listing_id )
LD_Review::get_ratings_for_comment( $comment_id )
LD_Review::save_ratings( $comment_id, $ratings )
LD_Review::recalculate_summary( $listing_id )

LD_Recaptcha

Wraps Google reCAPTCHA v3 verification for contact, claim, and report forms.

LD_Recaptcha::is_enabled()             // bool
LD_Recaptcha::get_site_key()           // string
LD_Recaptcha::verify( $token, $action ) // bool

Hooks Reference

Local Directory fires custom actions and filters that let you extend every major feature. Use these hooks from your theme's functions.php or a custom plugin.

Actions

Hook Parameters Description
ld_after_import_listing $listing_id, $raw_data Fires after a listing is created during import. Use it to sync external systems or add custom meta.
ld_claim_submitted $claim_id, $listing_id Fires when a new ownership claim is submitted. Useful for custom notifications.
ld_claim_approved $claim_id, $listing_id, $user_id Fires when an admin approves a claim. The listing author is changed to $user_id.
ld_claim_rejected $claim_id, $listing_id Fires when an admin rejects a claim.
ld_favorite_toggled $listing_id, $user_id, $is_favorite Fires when a user adds or removes a listing from favorites. $is_favorite is a boolean.
ld_listing_reported $report_id, $listing_id, $reason Fires when a visitor submits a report against a listing.

Example: Sync After Import

add_action( 'ld_after_import_listing', function( $listing_id, $raw_data ) {
    // Push new listing to external CRM
    my_crm_sync( $listing_id, $raw_data['name'] );
}, 10, 2 );

Filters

Hook Parameters Description
ld_badge_types $types Array of badge definitions (featured, verified, new, price). Add custom badges here.
ld_compare_fields $fields Array of fields shown in the comparison table. Add or remove comparison rows.
ld_import_field_mapping $mapping, $source_type Field mapping array for imports. Modify to map custom source fields to listing meta.
ld_import_filter_about_data $about_data, $raw_entry Filter the description/about text before saving during import. Useful for content cleanup.
ld_map_marker_data $marker, $listing Marker data array before it is sent to the map JavaScript. Add custom properties.
ld_schema_data $schema, $listing JSON-LD structured data array for a single listing. Add extra Schema.org properties.
ld_schema_category_map $map Mapping of category slugs to Schema.org types (e.g., 'restaurants' => 'Restaurant').
ld_search_query_args $args, $params WP_Query arguments before search execution. Add custom meta queries or tax queries.
ld_search_results $results, $query Search results array after query execution. Modify, re-sort, or inject promoted listings.
ld_template_path $path, $template_name Resolved template file path. Override to load templates from a custom location.

Example: Add a Custom Badge

add_filter( 'ld_badge_types', function( $types ) {
    $types['eco_friendly'] = [
        'label'    => 'Eco-Friendly',
        'color'    => '#16a34a',
        'bg_color' => '#f0fdf4',
        'icon'     => 'leaf',
        'meta_key' => '_ld_eco_friendly',
    ];
    return $types;
} );

AJAX Endpoints

AJAX handlers are registered in LD_Ajax for both authenticated (wp_ajax_) and public (wp_ajax_nopriv_) requests. All requests use admin-ajax.php and require a valid nonce unless noted otherwise.

Action Access Description
ld_search Public Performs a search query and returns listings as JSON (cards + markers).
ld_track_event Public Records an analytics event (view, click, phone, direction, share).
ld_toggle_favorite Public* Adds or removes a listing from the user's favorites. *Uses cookies for guests, user meta for logged-in users.
ld_submit_report Public Submits a report against a listing (spam, incorrect, closed, other).
ld_submit_claim Logged-in Submits an ownership claim. Sends a verification email to the claimant.
ld_verify_claim_code Logged-in Verifies a claim using the emailed code.
ld_get_compare_data Public Returns comparison data for an array of listing IDs.
ld_toggle_featured Admin Toggles the featured status of a listing (admin only).

Nonce verification: All AJAX handlers verify a nonce passed as _ajax_nonce or nonce. The nonce action is ld_ajax_nonce and is localized via window.ldData.nonce.

REST API

Local Directory registers 8 REST endpoints under the local-directory/v1 namespace. All endpoints support standard WordPress REST authentication (cookie, Application Password, or OAuth).

Route Methods Description
/local-directory/v1/listings GET Paginated listing collection. Supports per_page, page, category, location, orderby, search parameters.
/local-directory/v1/listings/{id} GET Single listing with all fields, location data, ratings, badges, and opening hours.
/local-directory/v1/categories GET Category taxonomy terms with icons, listing counts, and hierarchy.
/local-directory/v1/locations GET Location taxonomy terms with hierarchy and listing counts.
/local-directory/v1/reviews GET Reviews for a listing (?listing_id=). Includes multi-criteria ratings per review.
/local-directory/v1/reviews POST Submit a review with criteria ratings. Requires authentication.
/local-directory/v1/search GET Full search with keyword, category, location, radius, price, status, and bounds parameters.
/local-directory/v1/markers GET Lightweight marker data (id, lat, lng, title, icon) for map rendering. Supports the same filters as search.

Example: Fetch Listings

// JavaScript (fetch)
const res = await fetch('/wp-json/local-directory/v1/listings?per_page=10&category=restaurants');
const data = await res.json();

// PHP (internal)
$request  = new WP_REST_Request( 'GET', '/local-directory/v1/listings' );
$request->set_param( 'per_page', 10 );
$response = rest_do_request( $request );

Gutenberg Blocks

All shortcodes have matching Gutenberg block equivalents registered under the local-directory/ namespace. Block registration is handled in LD_Blocks.

Block Shortcode Equivalent Description
local-directory/directory [local-directory] Full directory page with map, search, and listing grid.
local-directory/listings [ld-listings] Standalone listing grid or list.
local-directory/map [ld-map] Standalone interactive map with markers.
local-directory/search [ld-search] Standalone search form.
local-directory/categories [ld-categories] Category grid with icons and counts.
local-directory/compare [ld-compare] Side-by-side listing comparison table.
local-directory/recently-viewed [ld-recently-viewed] Recently viewed listings from localStorage.

Server-side rendered: All blocks use register_block_type() with a render_callback that delegates to the corresponding shortcode controller. Block attributes map 1:1 to shortcode attributes.

Elementor Widgets

When Elementor is active, Local Directory registers 7 widgets in a dedicated "Local Directory" category. Widget files are in includes/elementor/widgets/.

Widget Class File
Directory LD_Elementor_Directory class-ld-elementor-directory.php
Listings LD_Elementor_Listings class-ld-elementor-listings.php
Map LD_Elementor_Map class-ld-elementor-map.php
Search LD_Elementor_Search class-ld-elementor-search.php
Categories LD_Elementor_Categories class-ld-elementor-categories.php
Compare LD_Elementor_Compare class-ld-elementor-compare.php
Recently Viewed LD_Elementor_Recently_Viewed class-ld-elementor-recently-viewed.php

Frontend Assets

All frontend scripts and styles are conditionally enqueued only on pages where directory content is rendered. Handles are prefixed with ld-.

Scripts

Handle File Notes
ld-frontend assets/js/frontend.js Core frontend logic: cards, favorites, compare tray, analytics tracking.
ld-map assets/js/map-leaflet.js or assets/js/map-google.js Map provider implementation. Resolved dynamically based on settings.
ld-search assets/js/search.js Search form logic, AJAX search, autocomplete, viewport search.
ld-compare assets/js/compare.js Comparison table rendering and localStorage management.
ld-single assets/js/single.js Single listing page: gallery, tabs, reviews, contact form, share, QR code.
ld-recaptcha Google reCAPTCHA v3 CDN Loaded only when reCAPTCHA is enabled and a form is present.

Styles

Handle File Notes
ld-frontend assets/css/frontend.css All frontend component styles. Uses CSS custom properties for theming.
ld-leaflet Leaflet CDN Loaded when Leaflet/OpenStreetMap is the selected map provider.
ld-markercluster Leaflet.markercluster CDN Marker clustering styles. Loaded with Leaflet provider.

Localized Data: window.ldData

The ld-frontend script receives a localized JavaScript object via wp_localize_script(). This object is available globally as window.ldData.

window.ldData = {
    ajaxUrl:       '/wp-admin/admin-ajax.php',
    restUrl:       '/wp-json/local-directory/v1/',
    nonce:         'abc123...',
    mapProvider:   'leaflet',           // 'leaflet' or 'google'
    googleApiKey:  '',                  // Google Maps API key (if set)
    mapStyle:      'default',           // Map style preset name
    defaultLat:    40.7128,             // Default map center latitude
    defaultLng:    -74.0060,            // Default map center longitude
    defaultZoom:   12,                  // Default map zoom level
    clustering:    true,                // Whether marker clustering is enabled
    directoryPage: '/directory/',       // Main directory page URL
    comparePage:   '/compare/',         // Compare page URL
    i18n: {
        search:        'Search',
        noResults:     'No listings found.',
        loading:       'Loading...',
        viewOnMap:     'View on Map',
        addToCompare:  'Add to Compare',
        removeCompare: 'Remove from Compare',
        directions:    'Get Directions',
        openNow:       'Open Now',
        closed:        'Closed',
        featured:      'Featured',
        verified:      'Verified',
        claimListing:  'Claim this listing',
        reportListing: 'Report this listing'
    }
};

Plugin File Structure

Full directory tree of the Local Directory plugin:

local-directory/
├── local-directory.php          # Main plugin file, bootstrap
├── uninstall.php                # Cleanup on uninstall
│
├── includes/
│   ├── class-ld-plugin.php          # Singleton, subsystem init
│   ├── class-ld-activator.php       # Activation: tables, defaults
│   ├── class-ld-deactivator.php     # Deactivation cleanup
│   ├── class-ld-autoloader.php      # PSR-4 style autoloader
│   ├── class-ld-shortcodes.php      # Shortcode registration
│   ├── class-ld-template-loader.php # Template resolution + overrides
│   ├── class-ld-ajax.php            # AJAX handler registration
│   ├── class-ld-rest-api.php        # REST endpoint registration
│   ├── class-ld-email-manager.php   # 9 email notification types
│   ├── class-ld-badges.php          # Badge system (featured, verified, new, price)
│   ├── class-ld-favorites.php       # Favorites (cookie + user meta)
│   ├── class-ld-compare-controller.php
│   │
│   ├── admin/
│   │   ├── class-ld-admin.php           # Admin menus, metaboxes
│   │   ├── class-ld-admin-settings.php  # Settings page (tabs)
│   │   ├── class-ld-admin-import.php    # Import UI + processor
│   │   └── class-ld-admin-analytics.php # Analytics dashboard
│   │
│   ├── frontend/
│   │   ├── class-ld-directory-controller.php   # [local-directory]
│   │   ├── class-ld-listings-controller.php    # [ld-listings]
│   │   ├── class-ld-map-controller.php         # [ld-map]
│   │   ├── class-ld-search-controller.php      # [ld-search]
│   │   ├── class-ld-categories-controller.php  # [ld-categories]
│   │   ├── class-ld-single-controller.php      # [ld-listing-page]
│   │   ├── class-ld-compare-controller.php     # [ld-compare]
│   │   └── class-ld-recently-viewed-controller.php
│   │
│   ├── models/
│   │   ├── class-ld-listing.php
│   │   ├── class-ld-location-data.php
│   │   ├── class-ld-claim.php
│   │   ├── class-ld-review.php
│   │   └── class-ld-report.php
│   │
│   ├── features/
│   │   ├── class-ld-claim-listing.php
│   │   ├── class-ld-report.php
│   │   ├── class-ld-recaptcha.php
│   │   └── class-ld-analytics-tracker.php
│   │
│   ├── search/
│   │   └── class-ld-search-query.php
│   │
│   ├── maps/
│   │   └── class-ld-map-provider.php
│   │
│   ├── seo/
│   │   ├── class-ld-schema.php
│   │   └── class-ld-open-graph.php
│   │
│   ├── blocks/
│   │   └── class-ld-blocks.php
│   │
│   └── elementor/
│       ├── class-ld-elementor.php
│       └── widgets/
│           ├── class-ld-elementor-directory.php
│           ├── class-ld-elementor-listings.php
│           ├── class-ld-elementor-map.php
│           ├── class-ld-elementor-search.php
│           ├── class-ld-elementor-categories.php
│           ├── class-ld-elementor-compare.php
│           └── class-ld-elementor-recently-viewed.php
│
├── templates/
│   ├── directory.php
│   ├── listings/
│   │   ├── card-grid.php
│   │   ├── card-list.php
│   │   └── pagination.php
│   ├── single/
│   │   ├── single-listing.php
│   │   ├── gallery.php
│   │   ├── tabs.php
│   │   ├── reviews.php
│   │   ├── contact-form.php
│   │   └── sidebar.php
│   ├── search/
│   │   └── search-form.php
│   ├── map/
│   │   └── map-canvas.php
│   ├── categories/
│   │   └── category-grid.php
│   ├── compare/
│   │   └── compare-table.php
│   └── recently-viewed/
│       └── recently-viewed.php
│
├── assets/
│   ├── css/
│   │   ├── frontend.css
│   │   └── admin.css
│   ├── js/
│   │   ├── frontend.js
│   │   ├── search.js
│   │   ├── map-leaflet.js
│   │   ├── map-google.js
│   │   ├── compare.js
│   │   ├── single.js
│   │   └── admin.js
│   └── images/
│       ├── marker-default.svg
│       └── no-image.svg
│
└── languages/
    └── local-directory.pot