JReviews logo Docs
Menu
Version

jreviews:listing:structured_data

Allows filtering the Schema.org structured-data array before JSON-LD conversion. The array represents the complete assembled output for the listing's primary schema type plus admin-configured enrichments; modify or remove entries to control what's emitted.

Filter
System
Since 6.0.0

Start here

Copy the boilerplate below, then add the logic you need. Return the modified first argument. New to hooks? Read the hooks primer first.

Fires when adding Schema.org structured data to listing detail pages, after data is generated but before JSON-LD conversion

Parameters

Name Type Description
$schemaArray array The assembled structured data array, before JSON-LD conversion
$listing \JReviews\App\Models\Listing The listing model being displayed

Boilerplate

Paste this into your hook file, then add your logic and return the modified first argument.

fwd_add_filter('jreviews:listing:structured_data', function($schemaArray, $listing)
{
    // Your code here
    
    return $schemaArray;
}, 20, 2);
Why the final numbers matter: , 20, N sets the priority and how many arguments your callback accepts. If you use both $filterResult and $listing, keep , 20, 2 so both are passed to your callback.

Examples

Add amenity features from a checkbox field

Convert selected checkbox option labels into Schema.org LocationFeatureSpecification entries under amenityFeature.

fwd_add_filter('jreviews:listing:structured_data', function (array $schemaArray, $listing): array {
    // Replace 6 with the listing type ID that owns your amenities field.
    // This guard prevents the customization from changing every listing type.
    $targetListingTypeId = 6;

    if ((int) ($listing->listing_type_id ?? 0) !== $targetListingTypeId) {
        return $schemaArray;
    }

    // Replace jr_amenities with your checkbox or multi-select custom field name.
    // getFieldText returns the selected option labels, not the stored option values.
    $amenityLabels = (array) $listing->getFieldText('jr_amenities', []);

    $features = [];

    foreach ($amenityLabels as $label) {
        $label = trim(strip_tags((string) $label));

        if ($label === '') {
            continue;
        }

        $features[] = [
            '@type' => 'LocationFeatureSpecification',
            'name' => $label,
            'value' => true,
        ];
    }

    if ($features === []) {
        return $schemaArray;
    }

    $existing = $schemaArray['amenityFeature'] ?? [];

    if ($existing && isset($existing['@type'])) {
        $existing = [$existing];
    }

    $schemaArray['amenityFeature'] = array_values(array_merge(
        (array) $existing,
        $features
    ));

    return $schemaArray;
}, 20, 2);

Merge official profile URLs into sameAs without replacing values already generated by JReviews or another customization.

fwd_add_filter('jreviews:listing:structured_data', function (array $schemaArray, $listing): array {
    // Replace 6 with the listing type ID that has these profile fields.
    $targetListingTypeId = 6;
    // Only listings from that type should receive these profile links.

    if ((int) ($listing->listing_type_id ?? 0) !== $targetListingTypeId) {
        return $schemaArray;
    }

    // Use fields that store official profile or same-entity URLs.
    $profileFields = ['jr_facebook', 'jr_instagram', 'jr_youtube'];
    $sameAs = (array) ($schemaArray['sameAs'] ?? []);

    foreach ($profileFields as $fieldName) {
        $url = trim((string) $listing->getFieldValue($fieldName, ''));

        if ($url !== '') {
            $sameAs[] = $url;
        }
    }

    if ($sameAs !== []) {
        $schemaArray['sameAs'] = array_values(array_unique($sameAs));
    }

    return $schemaArray;
}, 20, 2);

Add a measured additional property

Create one PropertyValue for a numeric custom field when the value needs to be calculated or controlled in code.

fwd_add_filter('jreviews:listing:structured_data', function (array $schemaArray, $listing): array {
    // Replace 6 with the listing type ID that has the measurement field.
    $targetListingTypeId = 6;
    // Only listings from that type should receive this measured property.

    if ((int) ($listing->listing_type_id ?? 0) !== $targetListingTypeId) {
        return $schemaArray;
    }

    // Replace jr_wall_height with your numeric custom field name.
    $height = $listing->getFieldValue('jr_wall_height');

    if (! is_numeric($height)) {
        return $schemaArray;
    }

    $properties = $schemaArray['additionalProperty'] ?? [];

    if ($properties && isset($properties['@type'])) {
        $properties = [$properties];
    }

    $properties[] = [
        '@type' => 'PropertyValue',
        'name' => 'Max. wall height',
        'value' => (float) $height,
        // MTR is the UN/CEFACT unit code for meters.
        'unitCode' => 'MTR',
        'unitText' => 'm',
    ];

    $schemaArray['additionalProperty'] = array_values((array) $properties);

    return $schemaArray;
}, 20, 2);
Development & Support
Hooks are for custom development and are not included with support. Use the examples as a starting point and test changes on a staging site first.

Source Files

  • app/Actions/AddListingDetailStructuredDataAction.php