Skip to content

Data Model

On this page

Data model reference

The plugin creates no database tables and no taxonomies. Everything lives in wp_posts, wp_postmeta and a handful
of options.

#Post type

Name osfec_offer
Public yes, has_archive = the archive slug
Rewrite /{archive_slug}/{post_name}/, with_front = false
show_in_rest true — so the CPT works in the Query Loop block and in page-builder loops
supports title only
Capabilities create_posts is do_not_allow; map_meta_cap is on
Menu icon dashicons-building

post_content holds the imported description (descriptionWebsite, falling back to description), run through
wp_kses_post(). post_title comes from portalWwwTitle / portalTitle, falling back to “type, city”.

The slug is built once, at creation, from the title plus the public offer number, and is never rewritten on update
a price change must not move the URL.

#Meta

Every key carries the _osfec_ prefix. Empty values are not stored: a field missing from the payload has no meta
row, so get_post_meta() returns ''. Zero is kept — the ground floor is a real value.

None of these are registered with register_post_meta(), and none are exposed through the REST API.

#Identity

Key Type Notes
_osfec_id int Esti offer ID. The upsert key
_osfec_number string Public offer number (numberExport, falling back to number)
_osfec_hash string MD5 of the mapper version plus the payload. Drives change detection
_osfec_synced_at int Unix timestamp of the last write

#Classification

Key Type Notes
_osfec_type slug Type name without the bracketed variant, slugified: mieszkanie
_osfec_type_label string Raw Esti type name: Działka (Rolna)
_osfec_subtype slug Slugified bracketed variant: rolna
_osfec_subtype_label string The variant itself: Rolna
_osfec_transaction slug sale, rent, purchase, lease
_osfec_market slug primary, secondary
_osfec_tags CSV Marketing labels from Esti

#Price and size

Key Type Notes
_osfec_price float Empty when the price is hidden in Esti or is not positive
_osfec_currency string PLN, EUR, USD
_osfec_price_m float Price per m² (pricePermeter)
_osfec_price_prev float Previous price, stored only when it was higher than the current one
_osfec_area float Total area
_osfec_area_usable float Usable area
_osfec_rooms int
_osfec_floor int Floor the unit is on
_osfec_floors int Floors in the building
_osfec_year int Year built

#Location

Key Type Notes
_osfec_city string locationExportCityName, falling back to locationCityName
_osfec_commune string
_osfec_county string
_osfec_province string
_osfec_street string
_osfec_postal string
_osfec_lat, _osfec_lng string Coordinates as strings

Location values are stored raw. Esti mixes upper case, lower case and trailing spaces; formatting is the template’s
job.

#Media

Key Type Notes
_osfec_photos JSON Array of full-size URLs on the Esti CDN (_max variant)
_osfec_thumb string The first photo as its _min variant
_osfec_video string External link
_osfec_tour string External link to a virtual tour

Read photos with osfec_photos() rather than decoding the JSON yourself.

#Dictionary-backed attributes

These hold numeric IDs, not labels. Resolve them with osfec_attribute_label(); an ID the dictionary cannot name
yields an empty string, and the caller is expected to drop the row rather than print a number at a buyer.

Key Esti payload key
_osfec_ground_type groundType
_osfec_ground_fencing groundFencing
_osfec_ground_shape groundShape
_osfec_ground_ownership groundOwnership
_osfec_building_type buildingType
_osfec_building_heating buildingHeating

#Agent

Key Type Notes
_osfec_agent_id int Esti agent ID (contactId)
_osfec_agent_name string
_osfec_agent_email string
_osfec_agent_phone string

The agent is meta, not a taxonomy, so filtering by agent runs through meta_query. The photo, role and office are
not per-offer meta: they come from the agent directory option, matched on _osfec_agent_id. osfec_agent() merges both
sources.

#Options

Option Autoloaded Contents
osfec_settings yes Every setting, one array. Read it through OSFEC_Settings::get() / ::value()
osfec_agents no Agent directory keyed by Esti agent ID: name, photo, position, office, email, phone
osfec_dictionary no Flattened Esti dictionary, id => label
osfec_runs no The last 20 sync runs, newest first, up to 200 events each
osfec_last_sync no Unix timestamp of the last successful cleanup
osfec_license_key no The activated license key
osfec_license_status no Last verdict from the license server: active, invalid or version_mismatch
osfec_license_expires no Date the subscription is paid to, empty for a lifetime license
osfec_license_type no License type as the server names it
osfec_license_allowed_version no Newest plugin version the license covers
osfec_license_activated_at no UTC datetime of the first successful activation

#Transients

Transient TTL Purpose
osfec_sync_ids 1 h Esti ID → post ID map for the current run
osfec_sync_seen 1 h Esti IDs seen so far in the current run; cleanup depends on it
osfec_sync_error 24 h Last failure message, shown on the settings screen
osfec_map_<key> 6 h Slug → raw value map for a meta key, e.g. osfec_map_city
osfec_map_type_options 6 h Type slug → label
osfec_map_subtypes 6 h Subtype slug → label
osfec_update_latest 12 h (1 h after a failure) plugin/latest payload from the license server. A site transient
osfec_update_info 12 h (1 h after a failure) plugin/info payload, feeding the “View details” screen. A site transient

The slug maps are flushed at the end of every successful run, so a new city shows up in the filter dropdowns as soon as
the run that imported it finishes.

#Scheduled events

Hook Schedule Argument
osfec_sync_start osfec_6h (every 6 hours)
osfec_sync_page single event, 30 s after the previous page zero-based page number
osfec_license_check daily

OSFEC_API::PER_PAGE is 100.

#Change detection

_osfec_hash is md5( OSFEC_MAPPER_VERSION . wp_json_encode( $offer ) ). When the stored hash matches, the offer is
counted as unchanged and nothing is written.

OSFEC_MAPPER_VERSION is a constant in the main plugin file. Bump it whenever the mapping changes: every hash then
differs and the whole catalogue is re-imported on the next run.

#Constants

Constant Value
OSFEC_VERSION Plugin version, also the asset cache buster
OSFEC_FILE Main plugin file
OSFEC_PATH Plugin directory, trailing slash
OSFEC_URL Plugin URL, trailing slash
OSFEC_MAPPER_VERSION Mapping revision, part of the hash
OSFEC_BASENAME plugin_basename() of the main file, the key core uses in the update transient

#Class map

Class Responsibility
OSFEC_CPT Post type and rewrite rules
OSFEC_API Esti HTTP client: fetch_page(), fetch_agents(), fetch_dictionary()
OSFEC_Mapper Payload → post fields and meta. No side effects
OSFEC_Sync Run loop, cron, upsert, cleanup
OSFEC_Log Run records and the log screen
OSFEC_License Key verification, the License tab, the gate in front of every run
OSFEC_Updater Updates from the license server: the update transient and plugins_api
OSFEC_Settings Settings screen, sanitization, manual sync
OSFEC_Query GET parameters → WP_Query arguments
OSFEC_Frontend template_include, thumbnails, assets, shortcode, block
OSFEC_Schema JSON-LD in wp_head

Every class exposes a static init() that registers its own hooks; the main file calls them in order on
plugins_loaded.

#No hooks of its own

The plugin fires no custom actions and applies no custom filters. Extension points are: the template overrides, the
osfec_* helper functions, the shortcode and block attributes, the CSS, and WordPress’s own hooks (pre_get_posts,
template_include, the_content, …). See Building a custom UI.