JReviews logo Docs

V5 to V6 Hooks Migration

Complete migration guide for JReviews 5 hooks to their JReviews 6 equivalents.

Overview

JReviews 6 introduces a modernized hook system with a new naming convention and helper functions. This guide maps v5 hooks to their v6 equivalents with complete code examples for LLM-assisted migration.

Events System Unchanged
The Events system (`JReviewsEvents*`) is identical between v5 and v6. Only hooks (filters/actions) require migration.

Variable Reference
For a complete list of variables available in templates and hooks, see Discover Data Available in Templates.

Quick Reference

File Location Change

Version File Path
V5 jreviews_overrides/filters/filter_functions.php
V6 jreviews_overrides/hooks.php

Syntax Changes

V5 Filter Registration:

Clickfwd\Hook\Filter::add('filter_name', function($data, $params) {
    return $data;
}, $priority = 10);

V6 Filter Registration:

fwd_add_filter('filter_name', function($data, $params) {
    return $data;
}, $priority = 20);

V5 Action Registration:

Clickfwd\Hook\Action::add('action_name', function($params) {
    // Execute action
}, $priority = 10);

V6 Action Registration:

fwd_add_action('action_name', function($params) {
    // Execute action
}, $priority = 20);
Priority Change
Default priority changed from 10 (v5) to 20 (v6). Adjust priorities to maintain execution order.

Naming Pattern Changes

V5 Pattern V6 Pattern Example
can_{action}_{entity} jreviews:permission:{entity}.{action} can_create_listingjreviews:permission:listing.create
trusted_on_{action} jreviews:permission:{entity}.autopublish trusted_on_create_listingjreviews:permission:listing.autopublish
{entity}_form_bottom jreviews:{entity}_form.bottom listing_form_bottomjreviews:listing_form.bottom
{entity}_submit_validation jreviews:{entity}_form.validation listing_submit_validationjreviews:listing_form.validation
before_theme_render_viewvars jreviews:view_data View data filtering
field_output{:name} jreviews:custom_field.output:{:name} Custom field output
Review hooks Comment hooks v6 uses "comment" for user reviews

Data Access Patterns

Critical Change
In V6, `$listing` and `$comment` are Eloquent model instances, NOT arrays. Use arrow syntax (`$listing->title`) instead of array syntax (`$listing['Listing']['title']`).

Listing Data: V5 vs V6

Data V5 (Array) V6 (Eloquent Model)
Listing ID $listing['Listing']['listing_id'] $listing->id
Title $listing['Listing']['title'] $listing->title
URL Alias $listing['Listing']['slug'] $listing->alias
Summary $listing['Listing']['summary'] $listing->summary
Description $listing['Listing']['description'] $listing->description
URL $listing['Listing']['url'] $listing->url
Views $listing['Listing']['hits'] $listing->views
Created $listing['Listing']['created'] $listing->created
Modified $listing['Listing']['modified'] $listing->updated
Category ID $listing['Category']['cat_id'] $listing->cat_id
Category Title $listing['Category']['title'] $listing->category_title
Directory ID $listing['Directory']['dir_id'] $listing->directory->id or $listing->category->dir_id
Directory Title $listing['Directory']['title'] $listing->directory->title
Listing Type ID $listing['ListingType']['listing_type_id'] $listing->listing_type_id
Listing Type Title $listing['ListingType']['title'] $listing->listing_type->title
Owner ID $listing['User']['user_id'] $listing->owner_id
Owner Name $listing['User']['name'] $listing->owner_name
Owner Email $listing['User']['email'] $listing->owner_email
User Rating Avg $listing['Review']['user_rating'] $listing->aggregates->user_rating
User Rating Count $listing['Review']['user_rating_count'] $listing->aggregates->user_rating_count
Editor Rating Avg $listing['Review']['editor_rating'] $listing->aggregates->editor_rating
Main Photo URL $listing['MainMedia']['media_info']['image']['url'] $listing->mainPhoto->url
Cover Photo URL $listing['CoverMedia']['media_info']['image']['url'] $listing->coverPhoto->url
Featured $listing['Listing']['featured'] $listing->featured
Status $listing['Listing']['state'] $listing->status
PaidListings Data $listing['Paid'] $listing->paid (array with same structure)

V6 Listing Helper Methods

// Status checks
$listing->isPublished()
$listing->isUnpublished()
$listing->isPending()
$listing->isDraft()
$listing->isTrashed()
$listing->isScheduled()
$listing->isExpired()

// Feature checks
$listing->allowsRatings()
$listing->allowsComments()
$listing->hasMainPhoto()
$listing->hasCoverPhoto()
$listing->hasLogoPhoto()
$listing->hasGalleryPhotos()

// Custom field access
$listing->getFieldValue('jr_fieldname')    // Raw value
$listing->getFieldText('jr_fieldname')     // Text/label for options
$listing->getField('jr_fieldname')         // Field configuration

// Media access
$listing->getPhotos()
$listing->getVideos()
$listing->getAudio()
$listing->getAttachments()

Comment/Review Data: V5 vs V6

Terminology Change
V6 uses `$comment` instead of `$review`. User reviews are now "user comments" and editor reviews are "editorial comments".
Data V5 (Array) V6 (Eloquent Model)
Review/Comment ID $review['Review']['review_id'] $comment->id
Title $review['Review']['title'] $comment->title
Comment Text $review['Review']['comments'] $comment->comment
Rating $review['Review']['rating'] $comment->rating
Status $review['Review']['published'] $comment->status
Created $review['Review']['created'] $comment->created
Modified $review['Review']['modified'] $comment->modified
Owner ID $review['User']['user_id'] $comment->owner_id
Owner Name $review['User']['name'] $comment->owner_name
Listing ID $review['Review']['listing_id'] $comment->listing_id

V6 Comment Helper Methods

// Status checks
$comment->isPublished()
$comment->isUnpublished()
$comment->isPendingModeration()
$comment->isTrashed()

// Type checks
$comment->isEditorComment()
$comment->isUserComment()

// Owner reply checks
$comment->hasPublishedOwnerReply()
$comment->hasOwnerReplyPendingModeration()

// Custom field access
$comment->getFieldValue('jr_fieldname')
$comment->getFieldText('jr_fieldname')

// Media access
$comment->getPhotos()
$comment->getVideos()

Service Access Changes

Authentication:

// V5
$auth = S2Object::make('auth');
$userId = $auth->id;
$isGuest = $auth->guest;

// V6
$auth = fwd_auth();
$userId = $auth->id;
$isGuest = $auth->guest;
// Or shortcuts:
$userId = fwd_user_id();

Permissions Messages:

// V5
$listingPermissions = (S2Object::make('perm'))->__('listing');
$listingPermissions->setMessage('Custom error message');
return false;

// V6 - Use validation hooks for messages, or return false from permission hooks
fwd_add_action('jreviews:listing_form.validation', function($params) {
    $params['validator']->errors()->add('general', 'Custom error message');
}, 20);

Complete Migration Examples

Example 1: One Listing Per User Limit

V5:

function one_listing_limit_registered_user($permission, $params)
{
    $listingPermissions = (S2Object::make('perm'))->__('listing');
    $auth = S2Object::make('auth');

    if ($auth->id > 0) {
        $Model = new S2Model();
        $query = sprintf(
            'SELECT count(*) FROM %s WHERE %s = %d AND state = 1',
            EverywhereComContentModel::_LISTING_TABLE,
            EverywhereComContentModel::_LISTING_USER_ID,
            $auth->id
        );
        $count = $Model->query($query, 'loadResult');

        if ($count > 0) {
            $listingPermissions->setMessage('You have reached the limit of one listing per user');
            return false;
        }
    }

    return $permission;
}

Clickfwd\Hook\Filter::add('can_create_listing', 'one_listing_limit_registered_user', 10);

V6:

fwd_add_filter('jreviews:permission:listing.create', function($permission, $params) {
    $auth = fwd_auth();

    if ($auth->id > 0) {
        $count = \JReviews\Models\Listing::query()
            ->where('owner_id', $auth->id)
            ->where('status', 1)
            ->count();

        if ($count > 0) {
            // Message will be shown via the form validation system
            return false;
        }
    }

    return $permission;
}, 20);

// Add custom message via validation hook
fwd_add_action('jreviews:listing_form.validation', function($params) {
    $auth = fwd_auth();

    if ($auth->id > 0) {
        $count = \JReviews\Models\Listing::query()
            ->where('owner_id', $auth->id)
            ->where('status', 1)
            ->count();

        if ($count > 0) {
            $params['validator']->errors()->add('general', 'You have reached the limit of one listing per user');
        }
    }
}, 20);

Example 2: Modify View Data

V5:

Clickfwd\Hook\Filter::add('before_theme_render_viewvars', function($viewVars, $params) {
    // Add custom data to listing detail page
    if ($params['controller'] == 'listings' && $params['action'] == 'detail') {
        if (isset($viewVars['listing'])) {
            $listing = $viewVars['listing'];
            $listingId = $listing['Listing']['listing_id'];
            $categoryId = $listing['Category']['cat_id'];

            // Add custom data
            $viewVars['customData'] = [
                'special_offer' => $categoryId == 5 ? 'Free shipping!' : null,
            ];
        }
    }
    return $viewVars;
}, 10);

V6:

fwd_add_filter('jreviews:view_data', function($data, $params) {
    $viewName = $params['normalizedName'] ?? '';

    // Add custom data to listing detail page
    if (str_contains($viewName, 'listing') && str_contains($viewName, 'detail')) {
        if (isset($data['listing'])) {
            $listing = $data['listing'];
            $listingId = $listing->id;
            $categoryId = $listing->cat_id;

            // Add custom data
            $data['customData'] = [
                'special_offer' => $categoryId == 5 ? 'Free shipping!' : null,
            ];
        }
    }
    return $data;
}, 20);

Example 3: Form Validation with Custom Fields

V5:

Clickfwd\Hook\Filter::add('listing_submit_validation', function($validation, $params) {
    $formData = $params['data'];
    $listingTypeId = $formData['Listing']['listing_type_id'] ?? null;

    // Require phone for listing type 3
    if ($listingTypeId == 3) {
        $phone = $formData['Field']['jr_phone'] ?? '';
        if (empty($phone)) {
            $validation[] = 'Phone number is required for this listing type';
        }
    }

    return $validation;
}, 10);

V6:

fwd_add_action('jreviews:listing_form.validation', function($params) {
    $formData = $params['data'];
    $listingTypeId = $formData['listing_type_id'] ?? null;

    // Require phone for listing type 3
    if ($listingTypeId == 3) {
        $phone = $formData['fields']['jr_phone'] ?? '';
        if (empty($phone)) {
            $params['validator']->errors()->add('jr_phone', 'Phone number is required for this listing type');
        }
    }
}, 20);

Example 4: Review Permission Based on Listing Data

V5:

Clickfwd\Hook\Filter::add('can_create_user_review_for_listing', function($permission, $params) {
    $listing = $params['listing'];

    // Disable reviews for closed listings
    $status = $listing['Field']['jr_status'] ?? '';
    if ($status == 'closed') {
        return false;
    }

    // Disable reviews for listing type 5
    $listingTypeId = $listing['ListingType']['listing_type_id'];
    if ($listingTypeId == 5) {
        return false;
    }

    return $permission;
}, 10);

V6:

fwd_add_filter('jreviews:permission:comment.create_user_comment', function($permission, $params) {
    $listing = $params['listing'];

    // Disable reviews for closed listings
    $status = $listing->getFieldValue('jr_status') ?? '';
    if ($status == 'closed') {
        return false;
    }

    // Disable reviews for listing type 5
    $listingTypeId = $listing->listing_type_id;
    if ($listingTypeId == 5) {
        return false;
    }

    return $permission;
}, 20);

Example 5: Custom Field Output Override

V5:

// Override output for jr_status field
Clickfwd\Hook\Filter::add('field_output:jr_status', function($output, $field, $entry, $instance, $name) {
    $value = $entry['Field']['jr_status'];
    $listingId = $entry['Listing']['listing_id'];

    $badges = [
        'available' => '<span class="badge badge-green">Available</span>',
        'pending' => '<span class="badge badge-yellow">Pending</span>',
        'sold' => '<span class="badge badge-red">Sold</span>',
    ];

    return $badges[$value] ?? $output;
}, 10);

V6:

// Override output for jr_status field
fwd_add_filter('jreviews:custom_field.output:jr_status', function($output, $params) {
    $listing = $params['listing'];
    $value = $listing->getFieldValue('jr_status');
    $listingId = $listing->id;

    $badges = [
        'available' => '<span class="badge badge-green">Available</span>',
        'pending' => '<span class="badge badge-yellow">Pending</span>',
        'sold' => '<span class="badge badge-red">Sold</span>',
    ];

    return $badges[$value] ?? $output;
}, 20);

Example 6: Auto-publish Based on User

V5:

Clickfwd\Hook\Filter::add('trusted_on_create_listing', function($trusted, $params) {
    $auth = S2Object::make('auth');

    // Auto-publish for specific user groups
    $trustedGroups = [8, 9, 10]; // Admin, Editor, Publisher

    foreach ($auth->groups as $groupId) {
        if (in_array($groupId, $trustedGroups)) {
            return true;
        }
    }

    return $trusted;
}, 10);

V6:

fwd_add_filter('jreviews:permission:listing.autopublish', function($trusted, $params) {
    $auth = fwd_auth();

    // Auto-publish for specific user groups
    $trustedGroups = [8, 9, 10]; // Admin, Editor, Publisher

    foreach ($auth->groups as $groupId) {
        if (in_array($groupId, $trustedGroups)) {
            return true;
        }
    }

    return $trusted;
}, 20);

Example 7: Add Content to Form

V5:

Clickfwd\Hook\Action::add('listing_form_bottom', function($params) {
    $listing = $params['listing'] ?? null;
    $listingTypeId = $params['listing_type_id'];

    if ($listingTypeId == 3) {
        echo '<div class="custom-notice">
            <p>Please ensure all contact information is accurate.</p>
        </div>';
    }
}, 10);

V6:

fwd_add_action('jreviews:listing_form.bottom', function($params) {
    $listing = $params['listing'] ?? null;
    $listingTypeId = $params['listing_type_id'];

    if ($listingTypeId == 3) {
        echo '<div class="custom-notice">
            <p>Please ensure all contact information is accurate.</p>
        </div>';
    }
}, 20);

Listing Permission Hooks

Permission hooks control who can perform actions on listings.

V5 Hook V6 Hook Notes
can_create_listing jreviews:permission:listing.create
can_update_listing jreviews:permission:listing.update
can_delete_listing jreviews:permission:listing.delete
can_publish_listing jreviews:permission:listing.publish
can_claim_listing jreviews:permission:listing.claim
can_favorite_listing jreviews:permission:listing.favorite
can_feature_listing jreviews:permission:listing.feature
can_send_listing_inquiry jreviews:permission:listing.inquire
can_update_listing_media jreviews:permission:listing.update_listing_media
can_upload_listing_media jreviews:permission:listing.upload_media Check $mediaType param
can_upload_listing_photo jreviews:permission:listing.upload_media Check $mediaType === 'photo'
can_upload_listing_video jreviews:permission:listing.upload_media Check $mediaType === 'video'
can_upload_listing_audio jreviews:permission:listing.upload_media Check $mediaType === 'audio'
can_upload_listing_attachment jreviews:permission:listing.upload_media Check $mediaType === 'attachment'
can_upload_media_from_url_in_listing jreviews:permission:listing.url_media_upload
can_use_editor_in_listing jreviews:permission:listing.allows_html_in_summary_description
can_add_listing_metadata jreviews:permission:listing.modify_metadata_fields
trusted_on_create_listing jreviews:permission:listing.autopublish
listing_allows_claims jreviews:permission:listing.claim Check listing type config
listing_allows_favorites jreviews:permission:listing.favorite Check listing type config
listing_allows_inquiries jreviews:permission:listing.inquire Check listing type config
listing_allows_user_reviews jreviews:permission:comment.create_user_comment Terminology change
listing_allows_editor_reviews jreviews:permission:comment.create_editorial_comment Terminology change

New V6 Listing Permission Hooks

V6 Hook Description
jreviews:permission:listing.preview_draft Permission to preview draft listings
jreviews:permission:listing.set_listing_title Permission to set custom title
jreviews:permission:listing.set_listing_title_alias Permission to set URL alias
jreviews:permission:listing.set_listing_publication_date Permission to set publication date
jreviews:permission:listing.set_listing_expiration_date Permission to set expiration date
jreviews:permission:listing.link_video Permission to link videos
jreviews:permission:listing.manage_media Permission to manage media
jreviews:permission:listing.media.autopublish Auto-publish listing media
jreviews:permission:listing.set_main_media Permission to set main media

Comment/Review Permission Hooks

Terminology Change
V6 uses "comment" instead of "review" in hook names. User reviews and editor reviews are now "user comments" and "editorial comments".
V5 Hook V6 Hook Notes
can_create_user_review_for_listing jreviews:permission:comment.create_user_comment
can_create_editor_review_for_listing jreviews:permission:comment.create_editorial_comment
can_update_review jreviews:permission:comment.update
can_delete_review jreviews:permission:comment.delete
can_vote_on_review jreviews:permission:comment.vote
can_report_review jreviews:permission:comment.report
can_reply_to_review jreviews:permission:comment.reply_as_owner
can_upload_review_media jreviews:permission:comment.upload_media Check $mediaType param
can_upload_review_photo jreviews:permission:comment.upload_media Check $mediaType === 'photo'
can_upload_review_video jreviews:permission:comment.upload_media Check $mediaType === 'video'
can_upload_review_audio jreviews:permission:comment.upload_media Check $mediaType === 'audio'
can_upload_review_attachment jreviews:permission:comment.upload_media Check $mediaType === 'attachment'
can_upload_media_from_url_in_review jreviews:permission:comment.url_media_upload
review_allows_voting jreviews:permission:comment.see_vote_widget Config-based
review_allows_discussions jreviews:permission:comment_discussion.create
review_allows_replies jreviews:permission:comment.reply_as_owner
review_allows_report jreviews:permission:comment.report
trusted_on_create_user_review jreviews:permission:comment.autopublish
trusted_on_create_editor_review jreviews:permission:comment.autopublish
trusted_on_update_user_review jreviews:permission:comment.autopublish
trusted_on_update_editor_review jreviews:permission:comment.autopublish
trusted_on_create_review_owner_reply jreviews:permission:comment.owner_reply.autopublish

New V6 Comment Permission Hooks

V6 Hook Description
jreviews:permission:comment.access_upload_form Permission to access media upload form
jreviews:permission:comment.allows_html_in_comment Allow HTML in comments
jreviews:permission:comment.link_video Permission to link videos
jreviews:permission:comment.manage_media Permission to manage comment media
jreviews:permission:comment.media.autopublish Auto-publish comment media
jreviews:permission:comment.see_comments_field Permission to see comments field
jreviews:permission:comment.update_editorial_comment Update editorial comments
jreviews:permission:comment.update_user_comment Update user comments

Comment Discussion Hooks

V5 Hook V6 Hook
can_delete_review_discussion jreviews:permission:comment_discussion.delete
can_update_review_discussion jreviews:permission:comment_discussion.update
can_report_review_discussion (removed)
trusted_on_create_review_discussion jreviews:permission:comment_discussion.autopublish
trusted_on_update_review_discussion jreviews:permission:comment_discussion.autopublish
review_discussion_allows_report (removed)

New V6 Comment Discussion Hooks

V6 Hook Description
jreviews:permission:comment_discussion.create Permission to create discussions
jreviews:permission:comment_discussion.allows_html_in_comment_discussion Allow HTML

Media Permission Hooks

V5 Hook V6 Hook
can_delete_media_in_listing jreviews:permission:media.delete
can_update_media_in_listing (use media.delete + re-upload)
can_publish_media_in_listing jreviews:permission:media.publish
can_download_attachment jreviews:permission:media.download
can_set_main_media jreviews:permission:media.set_main_media
can_vote_on_media jreviews:permission:media.like
can_report_media jreviews:permission:media.report
media_allows_voting jreviews:permission:media.like
media_allows_report jreviews:permission:media.report
trusted_on_create_photo jreviews:permission:listing.media.autopublish
trusted_on_create_video jreviews:permission:listing.media.autopublish
trusted_on_create_audio jreviews:permission:listing.media.autopublish
trusted_on_create_attachment jreviews:permission:listing.media.autopublish

New V6 Media Hooks

V6 Hook Description
jreviews:permission:media.set_media_function Set media function (cover, gallery, etc.)

Field Permission Hooks

V5 Hook V6 Hook
can_read_field jreviews:permission:field.read
can_write_field jreviews:permission:field.write
can_view_field_in_detail_page jreviews:permission:field.read
can_view_field_in_list_page jreviews:permission:field.read
can_view_field_in_comparison_page jreviews:permission:field.read

New V6 Field Hooks

V6 Hook Description
jreviews:permission:field.search Permission to use field in search

Form Hooks

Form hooks allow adding content and validating form submissions.

Listing Form

V5 Hook V6 Hook Type
listing_form_bottom jreviews:listing_form.bottom Action
listing_submit_validation jreviews:listing_form.validation Action
listing_save_pre jreviews:listing.data_before_save Filter

New V6 Listing Form Hooks

V6 Hook Type Description
jreviews:listing_form.top Action Content at top of form
jreviews:listing_form.before_field: Action Before specific field
jreviews:listing_form.after_field: Action After specific field
jreviews:listing_form.custom_fields Filter Filter custom fields collection
jreviews:listing_form.custom_fields:{:name} Filter Filter specific field

Comment/Review Form

V5 Hook V6 Hook Type
review_form_bottom jreviews:comment_form.bottom Action
review_submit_validation jreviews:comment_form.validation Action
review_save_pre (use events) Use CommentWasCreated event

New V6 Comment Form Hooks

V6 Hook Type Description
jreviews:comment_form.before_comment Action Content before comment field
jreviews:comment_form.before_field: Action Before specific field
jreviews:comment_form.after_field: Action After specific field

Owner Reply Form

V5 Hook V6 Hook
owner_reply_form_bottom jreviews:comment_owner_reply_form.bottom
owner_reply_submit_validation jreviews:comment_owner_reply_form.validation

Other Forms

V5 Hook V6 Hook
claim_form_bottom jreviews:listing_claim_form.bottom
claim_submit_validation jreviews:listing_claim_form.validation
inquiry_form_bottom jreviews:listing_inquiry_form.bottom
inquiry_submit_validation jreviews:listing_inquiry_form.validation
report_form_bottom jreviews:comment_report_form.bottom / jreviews:media_report_form.bottom
report_submit_validation jreviews:comment_report_form.validation / jreviews:media_report_form.validation
discussion_form_bottom (removed)
mylists_form_bottom mylists:list_form.bottom
mylists_submit_validation mylists:list_form.validation
resources_form_bottom listingresources:resource_form.bottom
resources_submit_validation listingresources:resource_form.validation

Custom Field Hooks

V5 Hook V6 Hook Notes
field_output{:name} jreviews:custom_field.output:{:name} Replace {:name} with field name
field_form{:name|:type} (see listing form hooks) Use form field hooks

Template Hooks

V5 Hook V6 Hook Notes
before_theme_render_viewvars jreviews:view_data View data filtering for all views
template_listings.detail:{position}-description jreviews:listing.below-description Position-specific
template_listings.{page}:after-title (use template overrides)
template_directory:after-directory{-name} (use template overrides)

New V6 Template Position Hooks

V6 Hook Description
jreviews:listing.below-description After listing description
jreviews:listing.below-fields After custom fields
jreviews:listing.below-userreviews After user reviews
jreviews:listing.below-editorreviews After editor reviews
jreviews:listing.below-bottommedia After bottom media
jreviews:listing.below-socialbookmarks After social bookmarks
jreviews:listing.detail.cover_photo Cover photo area
jreviews:listing.detail.map Map area
jreviews:listing:before_render Before listing renders

Meta Tags & SEO Hooks

V6 uses the unified jreviews:page_meta:{:name} hook pattern for all meta tags. Replace {:name} with the meta tag name.

V5 Hook V6 Hook
page_canonical_metatag jreviews:page_meta:canonical_url
page_title_metatag jreviews:page_meta:title
page_description_metatag jreviews:page_meta:description
page_keywords_metatag (removed) Keywords meta tag deprecated in modern SEO
page_robots_metatag jreviews:page_meta:robots
page_next_metatag (removed) Pagination handled differently
page_prev_metatag (removed) Pagination handled differently
open_graph_tags_before_parse jreviews:page_meta:og:title, jreviews:page_meta:og:description, jreviews:page_meta:og:image
open_graph_tags_after_parse (use hooks above) Same hooks, adjust priority
twitter_cards_before_parse jreviews:page_meta:twitter:title, jreviews:page_meta:twitter:description, jreviews:page_meta:twitter:image
twitter_cards_after_parse (use hooks above) Same hooks, adjust priority
list_page_ld_json_structured_data jreviews:listing:structured_data

Open Graph / Twitter Cards Migration Example

V5:

Clickfwd\Hook\Filter::add('open_graph_tags_before_parse', function($tags, $params) {
    $listing = $params['listing'] ?? null;

    if ($listing && $listing['Listing']['featured']) {
        $tags['og:title'] = 'Featured: ' . $listing['Listing']['title'];
    }

    return $tags;
}, 10);

V6:

// In V6, each Open Graph/Twitter tag has its own hook
fwd_add_filter('jreviews:page_meta:og:title', function($value, $name, $routeName, $attributes) {
    $listing = $attributes['listing'] ?? null;

    if ($listing && $listing->featured) {
        return 'Featured: ' . $listing->title;
    }

    return $value;
}, 20);

// Twitter cards work the same way
fwd_add_filter('jreviews:page_meta:twitter:title', function($value, $name, $routeName, $attributes) {
    $listing = $attributes['listing'] ?? null;

    if ($listing && $listing->featured) {
        return 'Featured: ' . $listing->title;
    }

    return $value;
}, 20);

// Customize OG image for specific categories
fwd_add_filter('jreviews:page_meta:og:image', function($value, $name, $routeName, $attributes) {
    $listing = $attributes['listing'] ?? null;

    if ($listing && $listing->cat_id == 5) {
        // Use category-specific default image if listing has no main photo
        if (! $listing->hasMainPhoto()) {
            return 'https://example.com/images/category-5-default.jpg';
        }
    }

    return $value;
}, 20);
Hook Parameters
V6 page meta hooks receive `$value`, `$name`, `$routeName`, and `$attributes`. The `$attributes` array contains contextual data like `listing`, `category`, `pagination`, etc.

System Hooks

V5 Hook V6 Hook Notes
site_init (removed) Use service providers
admin_init (removed) Use service providers
render (removed) Use view composers
before_filter_request (removed) Use middleware
after_filter_output (removed) Use middleware
before_theme_render_request (removed) Use middleware
cron_tasks (unchanged) Still available
cookie_consent jreviews:cookie_consent
enable_captcha (removed) Configure in settings
enable_image_lazyloading (removed) Configure in settings

New V6 Route Hooks

V6 Hook Description
jreviews:route_matched:{:name} Fires when a frontend route matches
jreviews:admin_route_matched:{:name} Fires when an admin route matches

PaidListings Hook Examples

PaidListings hooks demonstrate the data access pattern changes. Note how $listing['Paid'] array access in V5 becomes $listing->paid in V6.

Enable Claims For Paid Listings

V5:

Clickfwd\Hook\Filter::add('listing_allows_claims', function($allow, $params) {
    $listing = $params['listing'];

    $premiumPlanIds = [1, 2, 3];

    $allow = isset($listing['Paid']) && array_intersect($premiumPlanIds, $listing['Paid']['plan_ids']);

    return $allow;
}, 10);

V6:

fwd_add_filter('jreviews:permission:listing.claim', function($allow, $params) {
    $listing = $params['listing'];

    $premiumPlanIds = [1, 2, 3];

    // $listing->paid is the Paid array, same structure as V5
    $allow = ! empty($listing->paid) && array_intersect($premiumPlanIds, $listing->paid['plan_ids']);

    return $allow;
}, 20);

Enable Inquiries For Paid Listings

V5:

Clickfwd\Hook\Filter::add('listing_allows_inquiries', function($allow, $params) {
    $listing = $params['listing'];

    $premiumPlanIds = [1, 2, 3];

    $allow = isset($listing['Paid']) && array_intersect($premiumPlanIds, $listing['Paid']['plan_ids']);

    return $allow;
}, 10);

V6:

fwd_add_filter('jreviews:permission:listing.inquire', function($allow, $params) {
    $listing = $params['listing'];

    $premiumPlanIds = [1, 2, 3];

    $allow = ! empty($listing->paid) && array_intersect($premiumPlanIds, $listing->paid['plan_ids']);

    return $allow;
}, 20);

Enable Favorites For Paid Listings

V5:

Clickfwd\Hook\Filter::add('listing_allows_favorites', function($allow, $params) {
    $listing = $params['listing'];

    $premiumPlanIds = [1, 2, 3];

    $allow = isset($listing['Paid']) && array_intersect($premiumPlanIds, $listing['Paid']['plan_ids']);

    return $allow;
}, 10);

V6:

fwd_add_filter('jreviews:permission:listing.favorite', function($allow, $params) {
    $listing = $params['listing'];

    $premiumPlanIds = [1, 2, 3];

    $allow = ! empty($listing->paid) && array_intersect($premiumPlanIds, $listing->paid['plan_ids']);

    return $allow;
}, 20);

Add Custom Plan Labels For Paid Listings

V5:

function paid_plan_labels($labels, $params)
{
    $listing = $params['listing'];

    $planIds = $listing['Paid']['plan_ids'] ?? [];

    if (empty($planIds)) {
        return $labels;
    }

    $basicPlanId = 1;
    $premiumPlanId = 2;

    // Label color CSS classes: jrRed, jrOrange, jrBlue, jrGreen, jrBrown, jrPurple

    if (in_array($basicPlanId, $planIds)) {
        $labels['basic'] = [
            'class' => 'jrStatusLabel jrPurple',
            'text' => 'Basic Plan'
        ];
    }

    if (in_array($premiumPlanId, $planIds)) {
        $labels['premium'] = [
            'class' => 'jrStatusLabel jrGreen',
            'text' => 'Premium Plan'
        ];
    }

    return $labels;
}

Clickfwd\Hook\Filter::add('listing_status_labels', 'paid_plan_labels', 10);

V6:

fwd_add_filter('jreviews:listing.status_labels', function($labels, $params) {
    $listing = $params['listing'];

    $planIds = $listing->paid['plan_ids'] ?? [];

    if (empty($planIds)) {
        return $labels;
    }

    $basicPlanId = 1;
    $premiumPlanId = 2;

    if (in_array($basicPlanId, $planIds)) {
        $labels['basic'] = [
            'class' => 'jrStatusLabel jrPurple',
            'text' => 'Basic Plan'
        ];
    }

    if (in_array($premiumPlanId, $planIds)) {
        $labels['premium'] = [
            'class' => 'jrStatusLabel jrGreen',
            'text' => 'Premium Plan'
        ];
    }

    return $labels;
}, 20);

Custom Meta Description For Paid Listings

V5:

Clickfwd\Hook\Filter::add('page_description_metatag', function($value, $params) {
    $listing = $params['listing'] ?? null;

    // Only continue if it's a listing detail page
    if (! $listing) {
        return $value;
    }

    // Only continue if listing is in a paid category
    if (empty($listing['Paid'])) {
        return $value;
    }

    // Only continue if it's a featured listing
    if (! $listing['Listing']['featured']) {
        return "Generic meta description for listing {$listing['Listing']['title']}";
    }

    // Featured listings show the custom meta description or if empty the listing summary
    return $listing['Listing']['metadesc'] ?: substr($listing['Listing']['summary'], 0, 150).'...';
}, 10);

V6:

fwd_add_filter('jreviews:page_meta:description', function($value, $name, $routeName, $attributes) {
    $listing = $attributes['listing'] ?? null;

    // Only continue if it's a listing detail page
    if (! $listing) {
        return $value;
    }

    // Only continue if listing is in a paid category
    if (empty($listing->paid)) {
        return $value;
    }

    // Only continue if it's a featured listing
    if (! $listing->featured) {
        return "Generic meta description for listing {$listing->title}";
    }

    // Featured listings show the custom meta description or if empty the listing summary
    return $listing->metadesc ?: substr($listing->summary, 0, 150).'...';
}, 20);

Using PaidHelper in V6

The PaidHelper class works the same way in V6, but listing access uses arrow syntax:

V5:

<?php if (PaidHelper::getVar('inquiries', $listing) == 1): ?>
    // Enable inquiries feature
<?php endif; ?>

// Check plan IDs
if (array_intersect([1,2,3], $listing['Paid']['plan_ids'])) {
    // Premium plan features
}

V6:

<?php if (PaidHelper::getVar('inquiries', $listing) == 1): ?>
    // Enable inquiries feature
<?php endif; ?>

// Check plan IDs - note the arrow syntax for paid array
if (! empty($listing->paid) && array_intersect([1,2,3], $listing->paid['plan_ids'])) {
    // Premium plan features
}
PaidHelper Compatibility
The `PaidHelper::getVar()` method accepts both V5 arrays and V6 Eloquent models, making it the recommended approach for plan variable checks.

Add-on Specific Hooks

MyLists

V5 Hook V6 Hook
can_create_user_list mylists:permission:list.create_user_list
can_update_user_list mylists:permission:list.update
can_delete_user_list mylists:permission:list.delete
can_publish_user_list mylists:permission:list.publish
can_make_user_list_private mylists:permission:list.make_private
listing_allows_user_lists mylists:permission:list.manage_listings
trusted_on_user_list_create mylists:permission:list.autopublish

ListingResources

V5 Hook V6 Hook
can_create_listing_resource listingresources:permission:resource.create
can_update_listing_resource listingresources:permission:resource.update
can_delete_listing_resource listingresources:permission:resource.delete
listing_allows_resources (check listing type config)
trusted_on_listing_resource_create listingresources:permission:resource.autopublish

Events Calendar

V5 Hook V6 Hook
eventscalendar:day_view.after_title eventscalendar:day_view.after_title

New V6 Events Calendar Hooks

V6 Hook Description
eventscalendar:day-view.after-last-field After last field in day view
eventscalendar:month-view.before-event-title Before event title in month view
eventscalendar:month-view.after-event-title After event title in month view
eventscalendar:status-badge.before-status Before status badge
eventscalendar:status-badge.after-status After status badge

Removed V5 Hooks

The following hooks have been removed and do not have direct v6 equivalents:

V5 Hook Reason / Alternative
site_init Use service providers or middleware
admin_init Use service providers or middleware
render Use Blade view composers
before_filter_request Use middleware
after_filter_output Use middleware
before_theme_render_request Use middleware
page_keywords_metatag Keywords meta tag deprecated in modern SEO
page_next_metatag Pagination handled differently
page_prev_metatag Pagination handled differently
enable_captcha Configure via settings
enable_image_lazyloading Configure via settings
can_add_review_on_listing_create Configure via listing type settings
listing_editor_reviews_open Configure via listing type settings
listing_user_reviews_open Configure via listing type settings
signup_modal_benefits_desc Use jreviews:user_registration.signup_benefits
community (removed)
configure:community.register_url (removed)

Complete V6 Hooks Reference

For the complete list of v6 hooks with detailed documentation, see the V6 Hooks Directory.