suma-dealer-locator
Help customers find authorized TenPoint crossbow dealers by location and distance.
Overview
Version: 3.0.2
Purpose: Provide dealer search functionality with location-based filtering
Namespace: Suma\DealerLocator
Dependencies: Carbon Fields (bundled), WP REST API
Framework: Suma Framework with version checking
Features
- Location-based search: Find dealers within specified radius
- Distance calculation: Accurate distance computation using coordinates
- REST API integration: Public API endpoint for dealer queries
- Custom fields management: Carbon Fields for dealer data entry
- Visual Composer support: Legacy page builder integration
- Admin interface: Dedicated admin page for dealer management
- i18n ready: Internationalization support
File Structure
suma-dealer-locator/
├── suma-dealer-locator.php # Entry point with version checker
├── includes/
│ ├── class-suma-dealer-locator.php # Main plugin class (extends Base)
│ ├── class-activator.php # Plugin activation logic
│ ├── class-deactivator.php # Plugin deactivation logic
│ ├── class-i18n.php # Internationalization
│ ├── class-utils.php # Utility functions
│ ├── class-custom-routes.php # REST API endpoints
│ └── class-visual-composer.php # Visual Composer integration
├── admin/
│ └── class-admin.php # Admin functionality
├── frontend/
│ └── class-frontend.php # Frontend display
├── framework/
│ └── includes/
│ └── class-version.php # Version checking
├── vendor/
│ └── carbon-fields/ # Custom fields library
└── languages/ # Translation files
Main Classes
Suma_Dealer_Locator
Main plugin orchestrator that manages the plugin lifecycle.
namespace Suma\DealerLocator;
class Suma_Dealer_Locator extends Base {
// Manages activation, deactivation, and component loading
}
Activator
Handles plugin activation tasks.
public static function activate() {
// Create dealer custom post type
// Flush rewrite rules
// Set default options
}
Deactivator
Handles plugin deactivation tasks.
public static function deactivate() {
// Clean up transients
// Flush rewrite rules
// Preserve user data (never delete on deactivation)
}
REST API
Base URL
/wp-json/suma-dl/v2/
Authentication
Public endpoint (no authentication required)
Endpoint: Get Dealers
Method: POST
Route: /suma-dl/v2/get_dealers
Purpose: Find dealers within a specified radius of a location
Request Body (JSON)
{
"latitude": 39.9612,
"longitude": -82.9988,
"radius": 50,
"unit": "miles"
}
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
latitude | float | Yes | Latitude of search center point |
longitude | float | Yes | Longitude of search center point |
radius | int | No | Search radius (default: 25) |
unit | string | No | 'miles' or 'km' (default: 'miles') |
Response (JSON)
{
"success": true,
"dealers": [
{
"id": 123,
"name": "Crossbow Pro Shop",
"address": "123 Main St",
"city": "Columbus",
"state": "OH",
"zip": "43215",
"phone": "(614) 555-1234",
"website": "https://example.com",
"latitude": 39.9612,
"longitude": -82.9988,
"distance": 2.4,
"distance_unit": "miles"
}
],
"count": 1,
"max_results": 200
}
Response Limits
- Maximum results: 200 dealers
- Results ordered by distance (nearest first)
- Includes calculated distance for each dealer
Error Responses
Missing coordinates:
{
"success": false,
"error": "Latitude and longitude are required"
}
Invalid coordinates:
{
"success": false,
"error": "Invalid latitude or longitude values"
}
Custom Fields (Carbon Fields)
Dealer custom post type uses Carbon Fields for data entry.
Dealer Fields
| Field Name | Type | Description |
|---|---|---|
dealer_address | text | Street address |
dealer_address_2 | text | Address line 2 |
dealer_city | text | City |
dealer_state | text | State/Province |
dealer_zip | text | Postal code |
dealer_country | text | Country |
dealer_phone | text | Phone number |
dealer_email | text | Email address |
dealer_website | text | Website URL |
dealer_latitude | text | Latitude (auto-calculated) |
dealer_longitude | text | Longitude (auto-calculated) |
dealer_description | textarea | Dealer description |
dealer_logo | image | Dealer logo |
Auto-Geocoding
When a dealer address is saved, the plugin automatically:
- Geocodes the full address
- Stores latitude and longitude
- Validates coordinate accuracy
Distance Calculation
The plugin uses the Haversine formula for accurate distance calculations between coordinates.
Haversine Formula Implementation
function calculate_distance($lat1, $lon1, $lat2, $lon2, $unit = 'miles') {
$earth_radius = ($unit === 'km') ? 6371 : 3959; // km or miles
$lat_diff = deg2rad($lat2 - $lat1);
$lon_diff = deg2rad($lon2 - $lon1);
$a = sin($lat_diff / 2) * sin($lat_diff / 2) +
cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
sin($lon_diff / 2) * sin($lon_diff / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
$distance = $earth_radius * $c;
return round($distance, 2);
}
Admin Interface
Dealer Management
Located in: WP Admin → Dealers
- List all dealers
- Add new dealer
- Edit dealer information
- Delete dealers
- Bulk actions
- Search dealers
Custom Columns
| Column | Content |
|---|---|
| Title | Dealer name |
| Location | City, State |
| Phone | Phone number |
| Distance | (shown in search results) |
Frontend Integration
Theme Integration
The theme integrates the dealer locator via:
wp-content/themes/elementor/inc/class-dealer-locator.php
REST API Call Example
fetch('/wp-json/suma-dl/v2/get_dealers', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
latitude: 39.9612,
longitude: -82.9988,
radius: 50,
unit: 'miles'
})
})
.then(response => response.json())
.then(data => {
console.log('Found dealers:', data.dealers);
})
.catch(error => {
console.error('Error:', error);
});
JavaScript Integration
The theme provides a dealer search interface that:
- Gets user's current location (with permission)
- Or allows manual address/zip entry
- Geocodes the search location
- Calls the REST API endpoint
- Displays results on a map
- Lists dealers with distance
Visual Composer Integration
Legacy support for Visual Composer page builder.
Shortcode
[suma_dealer_locator]
Parameters
| Parameter | Default | Description |
|---|---|---|
radius | 25 | Default search radius |
unit | miles | Distance unit |
map_height | 400 | Map height in pixels |
Internationalization
Text Domain
suma-dealer-locator
Translation Files
Located in: languages/
Translatable Strings
All user-facing text is wrapped in translation functions:
__('Find a Dealer', 'suma-dealer-locator');
_e('Search Results', 'suma-dealer-locator');
_n('%s dealer found', '%s dealers found', $count, 'suma-dealer-locator');
Database Storage
Custom Post Type
Name: dealers
Supports: title, editor, thumbnail
Hierarchical: No
Public: Yes (front-end viewable)
Show in REST: Yes
Meta Keys
| Meta Key | Storage | Description |
|---|---|---|
_dealer_address | Post meta | Street address |
_dealer_city | Post meta | City |
_dealer_state | Post meta | State |
_dealer_zip | Post meta | Postal code |
_dealer_latitude | Post meta | Latitude coordinate |
_dealer_longitude | Post meta | Longitude coordinate |
_dealer_phone | Post meta | Phone number |
_dealer_email | Post meta | Email address |
_dealer_website | Post meta | Website URL |
Geocoding Service
The plugin uses a geocoding service to convert addresses to coordinates.
Service Options
- Google Maps Geocoding API (if API key configured)
- OpenStreetMap Nominatim (fallback, free)
Geocoding Process
- User enters/updates dealer address
- Plugin sends address to geocoding service
- Service returns latitude/longitude
- Coordinates saved to post meta
- Coordinates used for distance calculations
Performance Optimization
Caching
- Dealer coordinates cached in post meta
- Geocoding results cached to avoid API rate limits
- Database queries use indexed meta queries
Query Optimization
- Uses meta queries with coordinate boundaries
- Limits results to 200 dealers
- Calculates distance only for dealers within rough boundary
Transients
Dealer search results can be cached with transients:
$cache_key = 'dealers_' . md5($lat . $lon . $radius);
$results = get_transient($cache_key);
if (false === $results) {
$results = perform_dealer_search();
set_transient($cache_key, $results, HOUR_IN_SECONDS);
}
Security
Input Validation
- Latitude/longitude validated as floats
- Radius validated as positive integer
- Unit validated against allowed values
Sanitization
$latitude = floatval($_POST['latitude']);
$longitude = floatval($_POST['longitude']);
$radius = absint($_POST['radius']);
$unit = in_array($_POST['unit'], ['miles', 'km']) ? $_POST['unit'] : 'miles';
Output Escaping
All dealer data escaped before output:
echo esc_html($dealer['name']);
echo esc_url($dealer['website']);
echo esc_attr($dealer['phone']);
Troubleshooting
No Dealers Found
- Verify dealer addresses have valid coordinates
- Check radius is appropriate for dealer density
- Confirm geocoding service is working
- Review PHP error logs
Incorrect Distances
- Verify latitude/longitude accuracy
- Check unit parameter (miles vs km)
- Ensure Haversine formula implementation correct
REST API Errors
- Check permalink structure (Settings → Permalinks → Save)
- Verify REST API enabled
- Check for conflicting plugins
- Review server error logs
Version History
v3.0.2 (Current)
- Enhanced REST API with v2 namespace
- Improved distance calculation accuracy
- Carbon Fields integration
- Performance optimizations
v2.x
- Visual Composer integration
- Admin interface improvements
v1.0
- Initial release