Custom Field Output Formatting
Customize one field’s output with the shared Output Format setting, Early Access Blade templates, or Legacy PHP formatting—and migrate existing PHP customizations safely.
Overview
This article covers formatting the output of one custom field from its Advanced Settings. It does not control how fields and field groups are positioned on a page. For page-level placement, see Customize Layout of Custom Fields.
JReviews provides three approaches:
| Approach | Best for | Availability |
|---|---|---|
| Output Format | Simple HTML using tags such as {fieldtext} and {listing_id} |
Legacy and Early Access |
| Blade formatting | Conditions, loops, calculations, and reusable templates | Early Access listing detail page |
| PHP formatting | Existing complex formatting on legacy pages | Legacy pages |
The Blade and PHP settings can both remain visible in the control panel. The renderer used by the page determines which settings take effect. When the Early Access listing detail page renders a field, legacy PHP output format and Field output template customizations are not executed.
Choose a Formatting Approach
Start with Output Format when tags and HTML are enough. It works in both rendering systems and requires no migration.
Use Blade template code for short Early Access customizations stored with a field. Use a Field output Blade template file when code is longer or shared by several fields.
Keep PHP output format or a legacy Field output template only for pages that still use the legacy renderer. If you are enabling Early Access, follow the migration guide.
The settings are located under Custom Fields → Edit Field → Advanced Settings.
Formatting System Reference
Choose the rendering system used by the page you are customizing. Early Access is shown by default.
Early Access Blade Formatting
The two Blade settings are:
- Blade template code — write Blade directly in the field settings.
- Field output Blade template — select a reusable
.blade.phpfile from your overrides.
Inline Blade Template Code
Inline code receives the same variables as a template file. It does not need an @props declaration.
<span class="field-value">{{ $value }}</span>
Blade escapes {{ }} output. Use {!! !!} only when the value deliberately contains trusted HTML.
Reusable Blade Template Files
Place field-output templates in the namespace-mirrored component override directory.
Joomla
templates/jreviews_overrides/resources/views/components/field-output/my-template.blade.php
WordPress
jreviews_overrides/resources/views/components/field-output/my-template.blade.php
New files are discovered by a live directory scan and should appear in the dropdown without clearing a file registry.
Use a descriptive filename such as price-summary.blade.php. A template selected in the dropdown is resolved using its hyphenated name first and its original name second.
JReviews resolves Blade formatting in this order:
- Inline Blade template code
- The selected Field output Blade template file
- An automatic file named after the field, such as
jr_price.blade.php - The field's normal Output Format when no Blade override is selected or found
Inline code therefore takes precedence if both inline code and a template file are configured.
Available Blade Variables
| Variable | Value |
|---|---|
$attributes |
ComponentAttributeBag containing output attributes |
$field |
The current Field model; for example, $field->name and $field->title |
$data |
JSON-decoded data when the stored value is valid JSON; otherwise the normalized field value |
$value |
The current field value returned by the model |
$route |
The string 'listing.detail' on the listing detail route, otherwise null |
$listing |
Listing model for listing custom fields |
$comment |
Comment model for review/comment custom fields |
These variables are injected by the renderer. @props can document defaults in reusable files, but it does not create the variables.
Read Current and Related Fields
For the current field, use $value for its stored value or read its display text from the model:
{{ $listing->getFieldTextString($field->name) }}
Listing field methods include:
| Method | Result |
|---|---|
$listing->getFieldValue('jr_name') |
Stored value; an array for multiple-value fields |
$listing->getFieldText('jr_name') |
Display label or an array of labels |
$listing->getFieldTextString('jr_name') |
Display labels joined with commas |
$listing->getFieldOptionImage('jr_name') |
Selected option image where supported |
$listing->getField('jr_name') |
Field model |
$listing->getFields() |
Field collection |
The same methods are available on $comment for review fields:
{{ $comment->getFieldTextString('jr_recommend') }}
From a review field, access a field belonging to its listing through the related model:
{{ $comment->listing->getFieldTextString('jr_neighborhood') }}
There is no pre-populated sibling-fields variable. Read sibling fields from $listing or $comment.
Common Listing and Comment Attributes
The models also normalize commonly used attributes across Joomla and WordPress. Access them with object syntax:
| Listing example | Value |
|---|---|
$listing->id |
Listing ID |
$listing->title |
Title |
$listing->alias |
URL alias |
$listing->url |
Listing URL |
$listing->summary |
Summary HTML |
$listing->description |
Description HTML |
$listing->featured |
Featured status as a boolean |
$listing->views |
View count |
$listing->owner_id |
Owner user ID |
$listing->owner_name |
Normalized owner or guest name |
$listing->owner_email |
Normalized owner or guest email |
$listing->created, $listing->updated |
Date objects or null |
$listing->category_title |
Category title when loaded |
| Comment example | Value |
|---|---|
$comment->id |
Comment/review ID |
$comment->title |
Comment/review title |
$comment->comments |
Comment/review body |
$comment->rating |
Normalized overall rating or null |
$comment->url |
Comment/review URL |
$comment->comment_type |
Editor or user comment type |
$comment->listing_id |
Associated listing ID |
$comment->owner_id |
Author user ID |
$comment->owner_name |
Normalized author or guest name |
$comment->owner_email |
Normalized author or guest email |
$comment->created, $comment->updated |
Date objects or null |
Relationships use the same object syntax:
<a href="{{ $comment->listing->url }}">
{{ $comment->listing->title }}
</a>
@if ($comment->isUserComment())
<span>{{ $comment->owner_name }}</span>
@endif
<time datetime="{{ $listing->created?->toAtomString() }}">
{{ $listing->created?->format('M j, Y') }}
</time>
Other useful relations include $listing->user, $listing->listing_type, $comment->user, and $comment->listing_type. A relation or selected column may depend on how the model was loaded, so guard optional data and do not assume an undocumented property exists.
Blade Syntax
{{-- Escaped output --}}
{{ $value }}
{{-- Conditional --}}
@if ($listing->featured)
<span>Featured</span>
@endif
{{-- Multiple values --}}
@foreach ((array) $listing->getFieldText('jr_amenities') as $amenity)
<span>{{ $amenity }}</span>
@endforeach
{{-- PHP calculation --}}
@php
$count = count((array) $listing->getFieldValue('jr_amenities'));
@endphp
<span>{{ $count }} amenities</span>
Blade Recipes
Show output based on another field
@php
$deliveryMethods = (array) $listing->getFieldValue('jr_delivery_methods');
@endphp
@if (in_array('local-delivery', $deliveryMethods, true))
<span>{{ $listing->getFieldTextString($field->name) }}</span>
@endif
Combine several optional fields
@php
$parts = array_filter([
$listing->getFieldValue('jr_address'),
$listing->getFieldValue('jr_city'),
$listing->getFieldValue('jr_postcode'),
], fn ($part) => $part !== null && $part !== '');
@endphp
@if ($parts)
<span>{{ implode(', ', $parts) }}</span>
@endif
Calculate a percentage from sibling fields
@php
$regularPrice = (float) $listing->getFieldValue('jr_regular_price');
$salePrice = (float) $listing->getFieldValue('jr_sale_price');
$discountPercentage = $regularPrice > 0
? (($regularPrice - $salePrice) / $regularPrice) * 100
: null;
@endphp
@if ($discountPercentage !== null && $discountPercentage > 0)
<span class="discount">-{{ number_format($discountPercentage, 2) }}%</span>
@endif
This uses explicit numeric casts and protects against division by zero. Use names that describe what the fields contain; do not preserve a misleading legacy variable name merely because the old code used it.
Iterate FormBuilder data
@foreach (($data['items'] ?? $data ?? []) as $item)
<div class="inventory-item">
<span>{{ $item['name'] ?? '' }}</span>
<span>{{ number_format((float) ($item['price'] ?? 0), 2) }}</span>
</div>
@endforeach
Blade Limitations and Empty Values
- A writable field with a genuinely blank value is rejected before its Blade template runs. A Blade
@elseblock cannot provide placeholder text for that case through this formatting path. - A template that renders an empty string hides the complete field wrapper.
$routeis a string ornull, not a route object.- The renderer does not inject legacy
$output,$params,$fields,$image, or$CustomFieldsvariables. - Returning the legacy default
$outputhas no direct Blade equivalent. Blade creates the output instead of receiving prebuilt default markup.
Legacy PHP Formatting
The two legacy settings are:
- PHP output format — write PHP in the field settings. The editor already starts in PHP mode, so do not add an opening
<?phptag. - Field output template — select a reusable legacy
.thtmltemplate.
Inline PHP Output Format
Return a string:
return "<span>Label:</span> <span>{$text}</span>";
Or close PHP before writing markup:
?>
<span>Label:</span> <span><?php echo $text; ?></span>
Reusable Legacy Templates
Legacy field-format templates belong in the active legacy theme's fields_phpformat directory.
Joomla
templates/jreviews_overrides/views/themes/{your-theme}/fields_phpformat/my-template.thtml
WordPress
jreviews_overrides/views/themes/{your-theme}/fields_phpformat/my-template.thtml
Available Legacy Variables
| Variable | Value |
|---|---|
$name |
Custom field name |
$entry |
Associated listing or review array |
$listing |
Listing array for listing fields |
$review |
Review array for review fields |
$field |
Field configuration array |
$fields |
Fields data for the entry |
$value |
Selected stored value or array of values |
$text |
Current display text or array of text values |
$image |
Current option image name or array of names |
$output |
Standard prebuilt output |
$params |
Page context including route, controller, action, view variables, suffix, and list/detail context |
$CustomFields |
Legacy custom-fields helper |
$value, $text, and $image are arrays for checkbox and multiple-select fields and strings for single-value fields.
Legacy Recipes
Read another field
$city = $CustomFields->fieldValue('jr_city', $entry);
return $city ?: false;
Show output when another option is selected
$methods = (array) $CustomFields->fieldValue('jr_delivery_methods', $entry);
return in_array('local-delivery', $methods, true) ? $output : false;
Combine several fields
$parts = array_filter([
$CustomFields->fieldValue('jr_address', $entry),
$CustomFields->fieldValue('jr_city', $entry),
$CustomFields->fieldValue('jr_postcode', $entry),
]);
return $parts ? implode(', ', $parts) : false;
Calculate a value from two fields
$price = (float) $CustomFields->fieldValue('jr_price', $entry);
$quantity = (float) $CustomFields->fieldValue('jr_quantity', $entry);
$total = $price * $quantity;
return $total > 0 ? number_format($total, 2) : false;
Legacy recipes that return $output, construct routes with legacy helpers, or emit inline JavaScript require design decisions during migration; they are not mechanical syntax conversions.
Blade and Legacy PHP Compared
| Concern | Early Access Blade | Legacy PHP |
|---|---|---|
| Inline setting | Blade template code | PHP output format |
| Reusable file | .blade.php under resources/views/components/field-output |
.thtml under the legacy theme's fields_phpformat |
| Listing context | $listing model |
$listing / $entry array |
| Review context | $comment model |
$review / $entry array |
| Output | Blade renders the output | Code can return or echo output |
| FormBuilder JSON | Decoded in $data when valid JSON |
Decode $text manually |
| Escaping | {{ }} escapes by default |
The customization must escape output explicitly |
Migration Guide
Only fields with custom code in PHP output format or a selected legacy Field output template need migration. Fields that use only Output Format continue working.
When the Early Access listing detail page is enabled, legacy PHP formatting is ignored. The field falls back to its normal Output Format until a Blade override is configured.
Inventory Before Enabling Early Access
On a staging copy of the site:
- Open every listing and review field's Advanced Settings.
- Record fields containing PHP output format code or a selected Field output template.
- Save a copy of the code and note whether each field belongs to a listing or review.
- Record the field type and every sibling field, helper, route, or script the code uses.
- Include examples with blank, single, and multiple values in the test plan.
Legacy-to-Blade Variable Mapping
| Legacy use | Blade replacement | Migration note |
|---|---|---|
$name |
$field->name |
$field is now a model |
$value |
$value |
Close match, normalized by the model |
$text |
getFieldText() / getFieldTextString() |
Decide whether the old code needs stored data or display labels |
$entry |
$listing or $comment |
Choose based on field ownership |
$listing array |
$listing model |
Replace array access with object methods/properties |
$review |
$comment model |
Review context was renamed |
$field array |
$field model |
Array-key access is not portable |
$CustomFields->fieldValue() |
$listing->getFieldValue() or $comment->getFieldValue() |
Reads stored values |
$CustomFields->field() |
Usually getFieldText() |
Check whether legacy click-to-search or output reformatting was intended |
$image |
getFieldOptionImage() when appropriate |
No injected variable |
$fields |
->getFields() or ->getField() |
No injected variable |
$output |
No direct equivalent | Redesign the fallback because Blade creates the output |
$params |
No direct equivalent | Only the limited $route string or null is injected |
Do not mechanically rename $text, $field, $fields, $image, $output, or $params. Their meaning or shape changes and may require redesign.
Migration Workflow
- Decide whether the code belongs in inline Blade template code or a reusable file.
- Replace legacy field reads with methods on
$listingor$comment. - Replace array access with model properties and methods.
- Choose stored values (
getFieldValue) or display labels (getFieldText) intentionally. - Add guards for missing data, invalid numbers, division by zero, and multiple-value arrays.
- Escape output with
{{ }}unless trusted HTML is explicitly required. - Flag legacy routes, helper classes, JavaScript,
$output, and$paramsfor manual redesign. - Enable Early Access on staging, add the Blade code, and test all recorded scenarios.
Migration Example: Sibling-Field Calculation
Legacy PHP
$regularPrice = $CustomFields->fieldValue('jr_regular_price', $entry);
$salePrice = $CustomFields->fieldValue('jr_sale_price', $entry);
$discount = (($regularPrice - $salePrice) / $regularPrice) * 100;
return '<span class="discount">-'.number_format($discount, 2).'%</span>';
Early Access Blade
@php
$regularPrice = (float) $listing->getFieldValue('jr_regular_price');
$salePrice = (float) $listing->getFieldValue('jr_sale_price');
$discount = $regularPrice > 0
? (($regularPrice - $salePrice) / $regularPrice) * 100
: null;
@endphp
@if ($discount !== null && $discount > 0)
<span class="discount">-{{ number_format($discount, 2) }}%</span>
@endif
Migration Example: FormBuilder JSON
Legacy PHP
$items = json_decode($text, true);
foreach ($items as $item) {
echo '<div>'.htmlspecialchars($item['name']).'</div>';
}
Early Access Blade
@foreach (($data['items'] ?? $data ?? []) as $item)
<div>{{ $item['name'] ?? '' }}</div>
@endforeach
AI-Assisted Migration
AI can produce a useful first draft, but it cannot infer field ownership, field types, stored values, or the intent of an undocumented helper. Give it those details and review every unsupported dependency it identifies.
Copy this prompt and add the requested information and legacy code:
Convert the JReviews legacy custom-field PHP formatting below to Early Access Blade.
Context I will provide:
1. Is this a listing field or a review/comment field?
2. What is the current field name and field type?
3. For every sibling field used: field name, field type, and whether the code needs its stored value or display label.
4. Is the result going into inline "Blade template code" or a reusable .blade.php file?
JReviews Blade runtime rules:
- $listing and $comment are models, never legacy arrays.
- Use $listing->getFieldValue('jr_name') or $comment->getFieldValue('jr_name') for stored values.
- Use getFieldText(), getFieldTextString(), or getFieldOptionImage() only when their display result matches the legacy intent.
- A review field can read a listing field through $comment->listing.
- $field is a Field model, not the legacy field array.
- $data contains JSON-decoded data when the stored value is valid JSON; otherwise it is the normalized value.
- $route is 'listing.detail' or null, not a route object.
- There is no direct injected equivalent for legacy $output, $params, $fields, or $image. Flag each use for manual redesign unless a verified model method solves the exact requirement.
- Do not assume $text maps directly to $data. Decide whether it represented a stored value or a display label.
- A genuinely blank writable field is rejected before the Blade template runs, so the template cannot create a placeholder for that case.
Verified model attributes and relationships:
- Listing: $listing->id, title, alias, url, summary, description, featured, views, owner_id, owner_name, owner_email, created, updated, and category_title.
- Comment/review: $comment->id, title, comments (the body), rating, url, comment_type, listing_id, owner_id, owner_name, owner_email, created, and updated.
- Relationships: $comment->listing->title, $comment->listing->url, $listing->user, $listing->listing_type, $comment->user, and $comment->listing_type.
- Dates are date objects or null. Guard them, for example: $listing->created?->format('M j, Y').
- Useful model checks include $listing->isPublished(), $listing->isPending(), $comment->isPublished(), $comment->isUserComment(), and $comment->isEditorComment().
- Use only attributes or methods listed here or elsewhere in the supplied documentation. If the legacy code needs another property, mark it "needs model/API verification" instead of inventing a replacement.
Conversion requirements:
- Use object access and the documented model methods; never use legacy array syntax.
- Escape output with {{ }} by default. Use {!! !!} only for deliberately trusted HTML and explain why.
- Guard nulls, invalid numeric data, multiple-value arrays, and division by zero.
- Preserve the behavior, but improve misleading variable names when the calculation reveals their real meaning.
- Flag legacy helper classes, route builders, inline scripts, click-to-search behavior, and unsupported variables instead of inventing replacements.
- If any unsupported dependency remains, say that the conversion is incomplete.
Return:
1. The converted Blade code.
2. A mapping of every legacy variable/helper to its replacement or "manual redesign required."
3. Assumptions and unresolved items.
4. Test cases covering blank, normal, invalid, and multiple values where relevant, plus listing and review context where relevant.
Field context:
[DESCRIBE FIELD OWNERSHIP, TYPES, AND SIBLING FIELDS]
Legacy PHP code:
[PASTE CODE HERE]
Verify an AI Conversion
Before using converted code on production, confirm:
- [ ] No legacy array access remains on
$listing,$comment, or$field. - [ ] Every field read intentionally uses a stored value or display label.
- [ ]
$output,$params,$fields,$image, route helpers, and scripts were redesigned or explicitly removed. - [ ] Arithmetic handles blank, non-numeric, zero, and negative values appropriately.
- [ ] Multiple-value fields are treated as arrays.
- [ ] Dynamic output is escaped unless trusted HTML is required.
- [ ] Listing and review fields use the correct model.
- [ ] The result was tested on staging with Early Access enabled.
Related Documentation
- Custom Fields — create fields and configure their shared Output Format.
- Customize Layout of Custom Fields — position fields and groups in page templates.
- Discover Data Available in Templates — inspect the broader variables available to page templates.
- Early Access Features — enable the rewritten listing detail page and review its migration checklist.