Documentation · Technical Documentation
Data Model
This document describes the logical data model used by the ADP Car Market Hub plugin: which entities exist, how they map to WordPress storage and how the different storage layers (custom database tables, posts, postmeta and options) work together.
When to use this document
Read this document if you need to:
- Understand which entity (vehicle, lead, search agent, analytics event) lives in which table or post type.
- Decide where to read or write data when building a custom integration or report.
- Understand how vehicle field data is split between the dedicated
as24_vehiclestable and the legacy postmeta keys.
For raw column lists, see Database Schema. For the postmeta keys exposed in the admin metabox UI, see the import and template documents in this section.
Overview
The plugin distinguishes between four primary entities:
- Vehicle — a single car listing imported from AutoScout24.
- Lead — a contact-form or inquiry submission tied (optionally) to a vehicle.
- Search Agent — a visitor's saved search criteria with email notifications.
- Analytics Event — a single tracked event on the public site (page view, filter use, contact-open, etc.).
Each entity uses the storage layer that fits its access pattern best. Vehicles are split between WordPress posts (for permalinks, taxonomies, templates) and a dedicated relational table (for typed columns and fast queries). Leads are full WordPress posts. Search agents and analytics events live in their own custom tables.
Vehicle
A vehicle is the central entity of the plugin. It is represented by two tightly coupled records:
- A WordPress post of post type
as24ci_car. The post provides the permalink (/cars/<slug>/), the title and excerpt, the featured image, the editor content, the assigned taxonomy terms and the WordPress capability surface. - A row in the dedicated table
{$wpdb->prefix}as24_vehicles, owned byAS24CI\Vehicle_Repository. The row holds the typed vehicle field data (price, mileage, registration year, fuel type, equipment, etc.), the raw API payload, and metadata used for change detection.
The two records are linked by the post_id column, which has a unique
index on the table. Each row also has a unique as24_id column that
holds the AutoScout24 listing identifier.
Why a dedicated table
Vehicle field data was originally stored in wp_postmeta. The plugin
moved this data into a dedicated table so that:
- Typed columns (decimals for prices, integers for mileage, dates for
first registration) can be queried and sorted directly without the
cost and ambiguity of
meta_queryjoins. - Composite indexes such as
idx_make_model,idx_fuel_conditionandidx_status_liveaccelerate archive filtering. - Change detection (
content_hash) lives next to the data it describes.
For backwards compatibility the importer continues to write a small set of postmeta keys; see the next section.
Postmeta keys still written for vehicles
Even with the custom table in place, the following postmeta keys
remain on as24ci_car posts. They are intentionally preserved to
keep older integrations working and to avoid regressions in the
admin UI, the AI Assistant queue and the deletion flow.
| Postmeta key | Meaning |
|---|---|
_as24ci_listing_id | AutoScout24 listing identifier. Mirrors the as24_id column on the table. |
_as24ci_content_hash | SHA-style hash of the importable content. Used to skip unchanged listings on re-import. |
_as24ci_images_hash | Hash of the source image set. May hold a "pending" sentinel while the image queue is still working. |
_as24ci_image_ids | Array of WordPress attachment IDs for images imported by the plugin. |
_as24ci_manual_image_ids | Array of attachment IDs that were added manually via the gallery picker. |
_as24ci_lead_status | (Lead posts only.) Lead status: new, contacted, closed or spam. |
Manual gallery attachments tracked in _as24ci_manual_image_ids are
treated as user-owned and are not removed by the plugin's deletion
or uninstall code, even when imported attachments are removed.
Manual overrides
The vehicles table includes a manual_overrides JSON column. When an
administrator edits a vehicle field that the importer normally manages,
the change is stored as a key/value entry in this column. The
AS24CI\Vehicle_Field_Resolver reads from the row first and then
overlays any manual overrides so that subsequent imports do not erase
manual edits. The dbDelta safety net in
Vehicle_Repository::maybe_create_table() adds this column on
existing tables if it is missing.
Vehicle taxonomies
as24ci_car posts are tied to 15 non-hierarchical taxonomies registered
by AS24CI\Taxonomies:
as24ci_brand, as24ci_model, as24ci_body_type, as24ci_condition,
as24ci_fuel_type, as24ci_transmission, as24ci_drive,
as24ci_ext_color, as24ci_int_color, as24ci_emission_std,
as24ci_energy_label, as24ci_vehicle_cat, as24ci_warranty_type,
as24ci_warranty_det, as24ci_cyl_arrange.
Taxonomy terms power the archive filters and provide native WordPress
URLs (for example /as24ci_brand/<slug>/). Many of the same values
also exist as denormalised columns in the vehicles table (for example
make, fuel_type, transmission_type) so archive queries can run
without joining wp_term_relationships.
Vehicle capabilities
The as24ci_car post type uses custom capabilities of the form
as24ci_car / as24ci_cars (map_meta_cap is enabled). On
activation the plugin grants this capability set to administrators and
to the custom as24ci_editor role. Administrators additionally
receive the manage_as24_imports capability (Plugin::CAP_MANAGE),
which gates the importer, settings, tools and logs UI. WordPress
"Editor" users do not receive vehicle capabilities by default.
Lead
A lead represents a contact-form or inquiry submission. It is stored
as a WordPress post of post type as24ci_lead, registered by
AS24CI\Leads_CPT. The post stores the visitor's message in the
post content/excerpt and the sender's contact details in postmeta.
Lead status is stored in the _as24ci_lead_status postmeta key and
defaults to new. The valid values are exposed as constants on
AS24CI\Leads_CPT:
STATUS_NEW(new)STATUS_CONTACTED(contacted)STATUS_CLOSED(closed)STATUS_SPAM(spam)
Leads_CPT::get_lead_status() defends against invalid values by
falling back to new. Leads_CPT::update_lead_status() validates the
incoming value before writing it.
If a lead was submitted from a vehicle page, the related vehicle is referenced in the lead's postmeta so the admin Leads tab can display the originating listing.
Search Agent
A search agent represents a visitor's saved search criteria with an
email subscription. Search agents are stored in the dedicated table
{$wpdb->prefix}as24ci_search_agents, owned by
AS24CI\Search_Agent.
Each row contains:
nameandemail— visitor identity.criteria— the saved search filters (stored as text).token— opaque token used to confirm or unsubscribe.frequency— notification frequency (defaults todaily).status— one ofpending,active,inactiveorpaused, exposed as constants onAS24CI\Search_Agent.created_atandconfirmed_at— timestamps for the GDPR-compliant Double-Opt-In flow.
Because search agents contain personal data, the table is always
dropped during uninstall regardless of the
as24ci_delete_data_on_uninstall setting.
Analytics Event
An analytics event records a single visitor interaction (page view,
filter use, contact-open, etc.). Events are stored in the dedicated
table {$wpdb->prefix}as24ci_analytics, owned by
AS24CI\Analytics.
Each row contains:
post_id— the related vehicle post ID, or0for global events such as filter searches.event_type— the event identifier (the allowed values are validated against the class's ownALLOWED_EVENTSlist).extra_data— optional JSON-encoded payload.created_at— event timestamp.
Events are only recorded when Options::ANALYTICS_ENABLED is set to
1. A daily retention cleanup runs through the
as24ci_daily_cleanup cron hook. Like the search-agent table, the
analytics table is always dropped during uninstall because it can
contain visitor-tracking data.
Activation-managed pages
During activation the plugin can create three WordPress pages that host the public-facing shortcodes:
Cars([as24ci_archive]) — the main vehicle archive page.Compare Cars([as24ci_compare]) — the comparison page.Favorites([as24ci_favorites]) — the favourites/wishlist page.
The IDs are stored in the options as24ci_page_archive_id,
as24ci_page_compare_id and as24ci_page_favorites_id. These pages
are not strictly part of the data model; they are created so the
shortcodes have a default home, and they are the only WordPress pages
the plugin removes during a destructive uninstall.
Settings (options)
All user-configurable settings are stored in wp_options. The keys
are defined as constants on AS24CI\Options. Examples include:
- API and authentication:
as24ci_base_url,as24ci_token_url,as24ci_seller_ids,as24ci_client_id,as24ci_client_secret,as24ci_token_audience. - Importer behaviour:
as24ci_default_post_status,as24ci_default_post_author,as24ci_import_images,as24ci_max_images,as24ci_convert_to_webp,as24ci_webp_quality,as24ci_full_sync. - Scheduler:
as24ci_auto_import_enabled,as24ci_cron_schedule,as24ci_cron_start_time,as24ci_cron_custom_minutes,as24ci_cron_max_vehicles,as24ci_cron_token. - Feature toggles:
Options::FEATURE_*constants for sitemap, schema, favourites, compare, social share, PDF datasheet, search agent, analytics, AI Assistant, REST API, dashboard widget, etc. - Schema/version markers:
as24ci_db_version,as24ci_caps_version,as24ci_vehicles_db_version,as24ci_search_agent_db_version.
The full inventory and behaviour of these options is documented in the Options And Settings Storage document.
Transients
The plugin uses a small number of transients for short-lived state. The most relevant for the data model are:
as24ci_access_token— cached AutoScout24 OAuth access token.as24ci_cron_import_running— run-lock that prevents overlapping imports (Scheduler::LOCK_TRANSIENT, ~40 minute TTL).as24ci_image_queue_running— run-lock for the image queue worker.as24ci_batch_queue— queue payload for the manual Batch-Wizard.
Transients are deleted during deactivation and uninstall as appropriate.
Configuration reference
| Storage layer | Owner | Notes |
|---|---|---|
as24ci_car post + custom table row | AS24CI\CPT, AS24CI\Vehicle_Repository | Linked by post_id; one-to-one. |
_as24ci_listing_id, _as24ci_content_hash | AS24CI\Importer, AS24CI\Mapper | Backwards-compat postmeta on vehicle posts. |
_as24ci_image_ids, _as24ci_manual_image_ids | AS24CI\Image_Importer, AS24CI\CPT | Imported vs manual gallery; manual is never deleted. |
as24ci_lead post + postmeta | AS24CI\Leads_CPT, AS24CI\Contact_Form | Status in _as24ci_lead_status. |
{$wpdb->prefix}as24ci_search_agents | AS24CI\Search_Agent | Personal data; always dropped on uninstall. |
{$wpdb->prefix}as24ci_analytics | AS24CI\Analytics | Visitor data; always dropped on uninstall. |
wp_options (as24ci_*) | AS24CI\Options | Settings; deletion gated by as24ci_delete_data_on_uninstall. |
Transients (as24ci_*) | various | Short-lived state; cleared on deactivation/uninstall. |
Operational notes
- A vehicle exists as long as both its post and table row exist. The
AS24CI\Vehicle_Deletercleanup is wired during plugin construction so that every permanent-delete path (native WP delete, importer full sync, bulk action) goes through the same idempotent flow. - The repository keeps a small in-process cache (object cache group
as24ci_vehicles, ~1 hour TTL) for single-row lookups. Direct writes to the table from custom code bypass this cache; consider invalidating it manually when extending the repository. - The
raw_datacolumn on the vehicles table preserves the source payload from the import. This is useful for debugging mapping issues but should not be relied upon as a stable contract; the payload structure is determined by the AutoScout24 API. - Manual gallery images are deliberately treated as the administrator's property. They are not removed when imported images are deleted, and they are not removed during uninstall. Remove them manually from the WordPress Media Library if required.
Troubleshooting
- **A vehicle is missing field values on the frontend but exists in
the post list.** Check that the vehicles table contains a row with
the matching
post_id. If the row is absent, the listing was created outside the importer or the migration to the custom table did not complete. - Edits in the metabox are overwritten by the next import. Verify
that the affected field is being written into the
manual_overridesJSON column. The override layer requires themanual_overridescolumn to exist; the schema upgrade adds it if necessary. - An imported image disappears. The plugin only deletes
attachments tracked in
_as24ci_image_ids. Images that were added via the manual gallery (_as24ci_manual_image_ids) are preserved. Verify which list contains the affected attachment ID. - **Search-agent or analytics rows still appear after deleting the
plugin.** Confirm that
uninstall.phpran. The two tables are dropped unconditionally at uninstall. If uninstall did not execute (for example because the plugin was only deactivated), the rows remain in place.