Skip to main content
Skip to main content

Developers Doc

Property Engine: Render Pipeline and View Model

7 mins read 5 Views 3+

The Havenlytics property engine is the set of functions and renderers that turn a hvnly_property post into a rendered card or single-property page. This guide follows the render pipeline end to end — from the query, through the loop, into the view model, and out to the template — and documents the helper functions you call when extending it.

Note: The property engine functions live in includes/Functions/ and the renderers in includes/Frontend/. Every property field is stored as post meta on the hvnly_property CPT.

The render pipeline

Rendering a property archive follows four stages:

  1. Query — PropertyQueryExecutor::query() returns a WP_Query of post_type=hvnly_property.
  2. Loop — for each post, call hvnly_render_property_card( get_the_ID() ).
  3. View model — the renderer builds the card’s data through hvnly_get_property_data().
  4. Template — PropertyCardRenderer::render_property_card_dynamic() chooses the preset or default path and loads the matching templates/archive/** partials.

Single-property pages are handled by includes/Frontend/PropertySingleRenderer.php, reachable through the accessor hvnly_get_property_single_renderer().

The property view model: hvnly_get_property_data()

hvnly_get_property_data( $property_id ) (in template-functions.php) is the canonical, statically-cached view model for a property. Call it whenever you need a normalized array of a property’s display data rather than reading meta keys one by one. The returned array is filtered through hvnly_property_data.

Returned keys

KeyMeaning / source
idProperty post ID.
titleProperty title.
permalinkProperty URL.
excerptProperty excerpt.
priceFrom _hvnly_property_price.
addressFrom _hvnly_property_address_line_1.
phoneContact phone.
bedroomsBedroom count.
bathroomsBathroom count.
areaFrom _hvnly_property_sqft.
garageFrom _hvnly_property_garage_sqft.
is_featuredFeatured flag.
author_idAuthor (listing owner) ID.
featured_imageFeatured image.
gallery_image_idsAttachment IDs from the gallery SSOT loader.
gallery_orderGallery ordering.
locationsLocation terms.
categoriesCategory/type terms.
author_posts_urlAuthor archive URL.
current_filtersActive search filters in context.
view_typegrid / list / map context.

Performance: hvnly_get_property_data() is statically cached per property per request. Repeated calls for the same ID within one request are essentially free, so prefer it over re-reading individual meta keys in a loop.

Gallery SSOT: hvnly_get_property_gallery_ids()

hvnly_get_property_gallery_ids( $property_id ) (in template-functions.php) is the single source of truth for a property’s gallery. Every avatar-free image surface must route through it rather than reading meta directly.

Internally it locates the gallery field(s) from hvnly_property_builder.sections, parses the stored value with hvnly_parse_gallery_attachment_ids() (which accepts CSV, JSON, or a single ID), then normalizes the result with hvnly_normalize_gallery_attachment_ids() — deduplicating and keeping only IDs where wp_attachment_is_image() is true. The stored value itself lives under {group_base_id}_images as a CSV of attachment IDs.

Common mistake: Reading a raw {base}_images meta key and calling explode( ',', ... ) yourself. That bypasses image validation and deduplication. Always call hvnly_get_property_gallery_ids().

Core rendering & field helpers

FunctionRole
hvnly_render_property_card( $id )Renders a full property card (delegates to PropertyCardRenderer).
hvnly_render_field()Renders a single configured field via its template slug.
hvnly_is_field_configured()Checks whether a field type is present in the card configuration.
hvnly_has_avatar_section()Checks whether the card layout includes the avatar section.
hvnly_get_configured_field_types()Lists the field types currently configured on the card.
hvnly_resolve_field_meta( int $post_id, array $field, string $primary = '' )Group-aware meta resolver (in utility-functions.php) that honours group_base_id + metaKey.

hvnly_resolve_field_meta() is the correct way to read a builder field’s value, because it understands the group-identity metadata rather than assuming a fixed meta key — the same mechanism that keeps values intact across field renames.

Price helpers

  • hvnly_get_currency_settings() / hvnly_get_currency_symbol() — resolve the configured currency.
  • hvnly_format_price() — format a numeric price for display.
  • hvnly_resolve_property_price() — resolve a property’s effective price value.
  • hvnly_safe_price_to_float() — coerce a stored price string to a float safely.
  • hvnly_format_large_number_with_suffix() — abbreviate large numbers (e.g. K / M suffixes).

Taxonomies

Properties are organised by these taxonomies: hvnly_prop_deptshvnly_prop_typeshvnly_prop_locationshvnly_prop_featureshvnly_prop_reviewshvnly_prop_tagshvnly_prop_badges, and hvnly_prop_status, plus amenities.

Shortcodes & view models

Front-end entry points live in includes/Frontend/Shortcodes/ — PropertyGridPropertyListPropertySearchFeaturedProperties, a Registry, and an AbstractShortcode base. Their presentation logic is backed by view models in Frontend/ViewModels/, including SearchFilters and SidebarSearchFilters.

Code examples

Render property cards in a custom loop

<?php
/**
 * Render a custom grid of properties using the same renderer as the
 * core archive, so cards match the saved Card Builder layout.
 */
$q = new WP_Query( array(
    'post_type'      => 'hvnly_property',
    'post_status'    => 'publish',
    'posts_per_page' => 6,
) );

if ( $q->have_posts() ) {
    echo '<div class="my-property-grid">';
    while ( $q->have_posts() ) {
        $q->the_post();
        echo hvnly_render_property_card( get_the_ID() );
    }
    echo '</div>';
    wp_reset_postdata();
}

Read a property’s gallery through the SSOT

<?php
$ids = hvnly_get_property_gallery_ids( get_the_ID() );

foreach ( $ids as $attachment_id ) {
    echo wp_get_attachment_image( $attachment_id, 'medium' );
}

Filter the property view model

<?php
/**
 * Add a derived "price_per_sqft" value to every property view model.
 * Runs whenever hvnly_get_property_data() builds a property's data.
 */
add_filter( 'hvnly_property_data', function ( array $data ) {
    $price = hvnly_safe_price_to_float( $data['price'] );
    $area  = (float) $data['area'];

    $data['price_per_sqft'] = ( $area > 0 )
        ? hvnly_format_price( $price / $area )
        : '';

    return $data;
} );

Best practices

  • Always render cards with hvnly_render_property_card() so custom loops honour the Card Builder layout automatically.
  • Read display data from hvnly_get_property_data(), not from scattered meta reads.
  • Resolve builder-field values with hvnly_resolve_field_meta() to stay compatible with group renames.
  • Use the price helpers for anything money-related so currency settings and formatting stay consistent site-wide.

Performance notes

  • The view model and card renderer are request-scoped caches; keep heavy work inside the hvnly_property_data filter idempotent and cheap since it runs per property.
  • The gallery SSOT validates every attachment with wp_attachment_is_image(); avoid calling it in tight inner loops for the same property — cache the returned array.

Security notes

  • hvnly_render_property_card() returns markup that already escapes field output through its templates; if you add fields to the hvnly_property_data filter, escape them in your own templates.
  • Never trust raw gallery meta — routing through hvnly_get_property_gallery_ids() ensures only real image attachments are emitted.

Common mistakes

  • Bypassing hvnly_get_property_gallery_ids() and parsing the {base}_images CSV manually.
  • Reading builder fields by a guessed meta key instead of hvnly_resolve_field_meta().
  • Formatting prices with raw PHP number_format() instead of hvnly_format_price(), breaking currency configuration.

Need more help?

Can't find what you're looking for? Our team and community are here to help you ship faster.