Documentation · Technical Documentation

Database Schema

This document describes the custom database tables created by the ADP Car Market Hub plugin. All tables are managed with WordPress's dbDelta() helper and are created on plugin activation. Their schema versions are tracked in wp_options so that dbDelta can apply column changes safely on upgrade.

When to use this document

Read this document if you need to:

  • Plan backups, retention or compliance work for plugin data.
  • Build custom reports against the plugin's tables.
  • Diagnose failed schema upgrades or missing columns.
  • Understand which tables are dropped on uninstall and which are not.

For the logical entity model that uses these tables, see Data Model.

Overview

The plugin creates five custom tables:

Table suffixPurposeSchema-version option
as24_vehiclesVehicle field data, replacing the bulk of wp_postmeta usage.as24ci_vehicles_db_version
as24ci_analyticsAnalytics events for vehicle pages and global filter actions.as24ci_analytics_db_version
as24ci_search_agentsVisitor search-alert subscriptions (Smart Stock Alerts).as24ci_search_agent_db_version
as24ci_content_studio_jobsContent Studio generation jobs (one row per job).as24ci_content_studio_db_version
as24ci_content_studio_assetsContent Studio generated assets, linked to a job by job_id.as24ci_content_studio_db_version

Real table names use the WordPress prefix {$wpdb->prefix} (typically wp_). The analytics table's schema version is stored in as24ci_analytics_db_version (constant AS24CI\Analytics::DB_VERSION_KEY). The two Content Studio tables share a single schema-version option (as24ci_content_studio_db_version, current value 1.3.0) defined on AS24CI\Content_Studio_Options::DB_VERSION and managed by AS24CI\Content_Studio_Repository::maybe_create_tables().

The plugin also relies on the WordPress core tables wp_posts, wp_postmeta, wp_term_relationships, wp_term_taxonomy and wp_terms for the as24ci_car and as24ci_lead post types and the 15 vehicle taxonomies. Those tables follow the standard WordPress schema and are not redefined by the plugin.

All five custom tables are dropped automatically on uninstall (see the Uninstall behaviour section below), regardless of the as24ci_delete_data_on_uninstall setting.

Table: {$wpdb->prefix}as24_vehicles

Owner: AS24CI\Vehicle_Repository. Created via Vehicle_Repository::maybe_create_table() on activation and re-checked on subsequent runs. The current schema version constant is 1.1, stored in the as24ci_vehicles_db_version option.

The table holds one row per vehicle, linked to its as24ci_car post by the post_id column. The as24_id column holds the AutoScout24 listing identifier. Both columns have unique indexes.

Columns

ColumnTypeNotes
idbigint(20) unsignedPrimary key, auto-increment.
post_idbigint(20) unsignedWordPress post ID for the linked as24ci_car. Unique.
as24_idvarchar(64)AutoScout24 listing identifier. Unique. Defaults to empty string.
seller_idvarchar(64)AutoScout24 seller identifier.
makevarchar(100)
modelvarchar(100)
version_full_namevarchar(255)Full trim/version label as supplied by the API.
condition_typevarchar(50)E.g. new, used, demo (values mirror the source API).
body_typevarchar(50)
fuel_typevarchar(50)
transmission_typevarchar(50)
drive_typevarchar(50)
color_exteriorvarchar(100)
color_interiorvarchar(100)
doorstinyint(3) unsignedDefaults to 0.
seatstinyint(3) unsignedDefaults to 0.
mileageint(10) unsignedDefaults to 0.
cubic_capacityint(10) unsignedEngine displacement.
range_kmint(10) unsignedElectric range in kilometres.
first_registrationdateNullable.
first_reg_yearsmallint(5) unsignedYear component of first_registration. Defaults to 0.
power_kwsmallint(5) unsigned
power_hpsmallint(5) unsigned
co2_emissionsmallint(5) unsignedg/km.
pricedecimal(10,2)Sale price.
list_pricedecimal(10,2)List/MSRP price (if supplied).
currencychar(3)ISO 4217 code; defaults to EUR.
emission_standardvarchar(20)E.g. Euro 6.
energy_labelchar(5)A-G energy label.
battery_capacitydecimal(6,2)kWh.
vinvarchar(20)Vehicle identification number.
statusvarchar(30)Listing status as supplied by the API.
is_livetinyint(1)1 if the listing is currently live, 0 otherwise.
had_accidenttinyint(1)
equipment_codeslongtextJSON array of equipment codes. Nullable.
equipment_standardlongtextJSON list of standard equipment items. Nullable.
equipment_optionallongtextJSON list of optional equipment items. Nullable.
imageslongtextJSON list of image URLs as supplied by the API.
raw_datalongtextFull source payload retained for diagnostics.
manual_overrideslongtextJSON object of administrator-edited fields that override imported values.
content_hashvarchar(64)Hash used to skip re-imports of unchanged listings.
imported_atdatetimeDefaults to CURRENT_TIMESTAMP.
updated_atdatetimeDefaults to CURRENT_TIMESTAMP. See the implementation note below.

Implementation note: updated_at is created with only DEFAULT CURRENT_TIMESTAMP inside the dbDelta SQL because adding ON UPDATE CURRENT_TIMESTAMP directly causes dbDelta to re-issue an ALTER on every plugin load. The ON UPDATE semantics are applied through a one-shot ALTER after dbDelta runs.

Indexes

IndexTypeColumnsPurpose
PRIMARYPrimaryidSurrogate primary key.
uk_as24_idUniqueas24_idOne row per AutoScout24 listing.
uk_post_idUniquepost_idOne row per WordPress vehicle post.
idx_make_modelIndexmake, modelDrives make/model archive filters.
idx_priceIndexpricePrice sort/range filtering.
idx_mileageIndexmileageMileage sort/range filtering.
idx_first_reg_yearIndexfirst_reg_yearYear sort/range filtering.
idx_fuel_conditionIndexfuel_type, condition_typeCommon combined filter.
idx_status_liveIndexstatus, is_liveLive-listing queries.
idx_content_hashIndexcontent_hashLookups during change detection.

Safety net for manual_overrides

After dbDelta runs, the repository performs a SHOW COLUMNS ... LIKE 'manual_overrides' check and, if the column is missing, issues an explicit ALTER TABLE ... ADD COLUMN manual_overrides longtext DEFAULT NULL AFTER raw_data. This compensates for occasional column-detection issues in some MySQL/MariaDB combinations and ensures the manual override layer always has a place to store data.

Table: {$wpdb->prefix}as24ci_analytics

Owner: AS24CI\Analytics. Created via Analytics::maybe_create_table() during activation and re-checked on every admin page load (the method is also wired to admin_init so existing installations pick up new columns without a manual re-activation).

Columns

ColumnTypeNotes
idbigint(20) unsignedPrimary key, auto-increment.
post_idbigint(20) unsignedVehicle post ID. 0 for global events such as filter searches.
event_typevarchar(20)Event identifier; defaults to view. Validated against ALLOWED_EVENTS.
extra_datalongtextOptional JSON-encoded payload. Nullable.
created_atdatetimeDefaults to CURRENT_TIMESTAMP.

Indexes

IndexTypeColumns
PRIMARYPrimaryid
post_event_dateIndexpost_id, event_type, created_at
event_typeIndexevent_type, created_at
created_atIndexcreated_at

Retention

Inserts only happen when Options::ANALYTICS_ENABLED is 1. A daily retention job runs through the as24ci_daily_cleanup cron hook (scheduled on activation, removed on deactivation). The retention window is configured through the analytics admin tab.

Table: {$wpdb->prefix}as24ci_search_agents

Owner: AS24CI\Search_Agent. Created via Search_Agent::maybe_create_table() on init (priority 1) so it is available for both admin and frontend AJAX handlers. The current schema version is 1.1, stored in the as24ci_search_agent_db_version option. Creation is gated by the Options::FEATURE_SEARCH_AGENT toggle, but the schema is registered unconditionally when the feature class is loaded.

Columns

ColumnTypeNotes
idBIGINT UNSIGNEDPrimary key, auto-increment.
nameVARCHAR(255)Visitor display name; defaults to empty string.
emailVARCHAR(255)Visitor email address.
criteriaTEXTSaved search criteria (serialised representation).
tokenVARCHAR(64)Token for confirmation and unsubscribe links.
frequencyVARCHAR(20)Notification frequency; defaults to daily.
statusVARCHAR(20)One of pending, active, inactive, paused. Defaults to pending.
created_atDATETIMEDefaults to CURRENT_TIMESTAMP.
confirmed_atDATETIMENullable; set when the visitor confirms via Double-Opt-In.

Indexes

IndexTypeColumns
PRIMARYPrimaryid
idx_statusIndexstatus
idx_tokenIndextoken

Privacy

Because the table contains personal data (name, email, search preferences), it is dropped during uninstall regardless of the as24ci_delete_data_on_uninstall setting. Site operators are responsible for additional retention or export tooling required by local privacy regulations.

Table: {$wpdb->prefix}as24ci_content_studio_jobs

Owner: AS24CI\Content_Studio_Repository. Created via Content_Studio_Repository::maybe_create_tables() (registered as its own activation hook in the main plugin file and also called lazily by most repository methods). Holds one row per Content Studio generation job. Status values are draft, queued, processing, ready and failed.

Columns

ColumnTypeNotes
idbigint(20) unsignedPrimary key, auto-increment.
vehicle_post_idbigint(20) unsignedSource vehicle post. Defaults to 0.
vehicle_snapshot_hashvarchar(64)Hash of the vehicle snapshot used for the job.
content_typevarchar(40)Requested content type.
platform_presetvarchar(40)Target platform preset.
output_formatvarchar(20)Output format.
preset_keyvarchar(80)Preset catalogue key.
statusvarchar(20)Defaults to draft.
prompt_payloadlongtextJSON prompt payload. Nullable.
providervarchar(40)Defaults to google.
modelvarchar(80)Provider model.
languagevarchar(20)Output language.
tonevarchar(40)Tone of voice.
attemptssmallint(5) unsignedProcessing attempts. Defaults to 0.
last_errortextLast error message. Nullable.
created_bybigint(20) unsignedWordPress user ID. Defaults to 0.
created_atdatetimeDefaults to CURRENT_TIMESTAMP.
updated_atdatetimeDefaults to CURRENT_TIMESTAMP.
started_atdatetimeNullable.
completed_atdatetimeNullable.

Indexes: PRIMARY (id), vehicle_post_id, status_created (status, created_at), platform_preset, content_type.

Table: {$wpdb->prefix}as24ci_content_studio_assets

Owner: AS24CI\Content_Studio_Repository. Holds the generated assets (text, images, copy packages) belonging to a job, linked by job_id.

Columns

ColumnTypeNotes
idbigint(20) unsignedPrimary key, auto-increment.
job_idbigint(20) unsignedParent job. Defaults to 0.
vehicle_post_idbigint(20) unsignedSource vehicle post. Defaults to 0.
asset_typevarchar(40)Asset type.
file_pathtextLocal file path. Nullable.
file_urltextPublic URL. Nullable.
mime_typevarchar(120)MIME type.
file_sizebigint(20) unsignedBytes. Defaults to 0.
widthint(10) unsignedImage width. Defaults to 0.
heightint(10) unsignedImage height. Defaults to 0.
duration_secondsdecimal(8,2)Media duration. Defaults to 0.00.
captionlongtextNullable.
hashtagslongtextNullable.
alt_texttextNullable.
thumbnail_pathtextNullable.
payload_jsonlongtextStructured payload (e.g. copy packages). Nullable.
scheduled_atdatetimeNullable.
schedule_statusvarchar(32)Defaults to unscheduled.
created_atdatetimeDefaults to CURRENT_TIMESTAMP.

Indexes: PRIMARY (id), job_id, vehicle_post_id, asset_type, created_at, schedule_status, scheduled_at.

WordPress core tables used by the plugin

The plugin does not modify the schema of WordPress core tables. It relies on them as follows:

  • wp_posts — stores as24ci_car (vehicles) and as24ci_lead (leads) posts.
  • wp_postmeta — stores backwards-compatibility postmeta for vehicle posts (_as24ci_listing_id, _as24ci_content_hash, _as24ci_images_hash, _as24ci_image_ids, _as24ci_manual_image_ids) and the lead status (_as24ci_lead_status).
  • wp_terms, wp_term_taxonomy, wp_term_relationships — store the 15 vehicle taxonomies registered by AS24CI\Taxonomies.
  • wp_options — stores all plugin settings (keys defined as constants on AS24CI\Options) plus the schema-version markers and the IDs of the activation-created pages.
  • wp_users and wp_usermeta — used indirectly via WordPress capabilities; no plugin-specific writes.

The custom roles and capabilities created by the plugin (as24ci_editor, the as24ci_car/as24ci_cars capability set and manage_as24_imports) are stored through the standard WordPress roles option (wp_user_roles).

Uninstall behaviour

The uninstall.php script always issues DROP TABLE IF EXISTS for the five custom tables and removes the matching schema-version options:

  • {$wpdb->prefix}as24_vehicles (and as24ci_vehicles_db_version)
  • {$wpdb->prefix}as24ci_analytics
  • {$wpdb->prefix}as24ci_search_agents (and as24ci_search_agent_db_version)
  • {$wpdb->prefix}as24ci_content_studio_assets
  • {$wpdb->prefix}as24ci_content_studio_jobs (and as24ci_content_studio_db_version)

On multisite installations the script switches to each site and repeats the cleanup. Posts, attachments and activation-created pages are only removed when the administrator has set as24ci_delete_data_on_uninstall to 1.

Operational notes

  • Schema upgrades are idempotent. Each owner class checks its schema-version option and quick-exits when the table already matches the expected version. The vehicles repository additionally checks that the table physically exists before quick-exiting.
  • Direct wpdb queries against these tables exist in the repository, analytics, search-agent and uninstall code paths. They are guarded with PHPCS ignore comments where appropriate. Custom code that bypasses the repository should treat the schema as part of the plugin's internal contract and be prepared for column changes between releases.
  • The raw_data, equipment_* and images columns hold serialised data. Treat them as opaque blobs and access them through the repository helpers when possible.
  • The manual_overrides column is the one place that mixes administrator-controlled and importer-controlled state. Avoid writing to it directly without going through the override layer.

Troubleshooting

  • maybe_create_table() did not create the vehicles table. Confirm that dbDelta() reported no errors. The repository writes errors to error_log() because it runs before the plugin Logger is available. Re-running activation (deactivate, activate) re-runs maybe_create_table().
  • A new column is missing after a plugin upgrade. Check the schema-version option for the affected table. If it equals the current value but the column is missing, delete the option to force dbDelta to re-run on the next admin page load. The vehicles table has an explicit manual_overrides safety net for this case.
  • The analytics table grows quickly. Reduce the retention window or disable analytics via Options::ANALYTICS_ENABLED. The daily cleanup job is required to keep the table bounded.
  • Search-agent rows persist after uninstall. Verify that WordPress executed uninstall.php. If only deactivate() ran (for example because the plugin was deactivated but not deleted), the table is intentionally retained until uninstall.