Very few use custom fields only in the administration area. As usual, an output in the frontend is required. We will address this question in the current part of the article series. How and where are custom fields in Joomla displayed in the frontend?
For impatient people: View the changed program code in the Diff View[^codeberg.org/astrid/j4examplecode/compare/t14a...t14b] and copy these changes into your development version.
Step by step
New files
No new files are added in this chapter.
Modified files
components/com_foos/src/View/Foo/HtmlView.php
Custom Fields display data in the frontend using events. The custom fields are displayed in three different places on the website. By default, the data is displayed before the content. This setting can be changed. Therefore we save the results of onContentAfterTitle
, onContentBeforeDisplay
and onContentAfterDisplay
. We do this in the View
.
Specifically, we make sure that the events
- onContentAfterTitle[^docs.joomla.org/Plugin/Events/Content#onContentAfterTitle],
- onContentBeforeDisplay[^docs.joomla.org/Plugin/Events/Content#onContentBeforeDisplay] und
- onContentAfterDisplay[^docs.joomla.org/Plugin/Events/Content#onContentAfterDisplay]
are triggered and the result is stored in a variable.
Joomla uses the observer design pattern[^en.wikipedia.org/wiki/Observer_pattern] for event handling. This is a software design pattern where an object maintains a list of its dependents called observers and automatically notifies them of state changes, usually by calling one of their methods.
components/com_foos/src/View/Foo/HtmlView.php
\defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Factory;
/**
* HTML Foos View class for the Foo component
class HtmlView extends BaseHtmlView
*/
public function display($tpl = null)
{
$this->item = $this->get('Item');
$item = $this->item = $this->get('Item');
Factory::getApplication()->triggerEvent('onContentPrepare', ['com_foos.foo', &$item, &$item->params]);
// Store the events for later
$item->event = new \stdClass;
$results = Factory::getApplication()->triggerEvent('onContentAfterTitle', ['com_foos.foo', &$item, &$item->params]);
$item->event->afterDisplayTitle = trim(implode("\n", $results));
$results = Factory::getApplication()->triggerEvent('onContentBeforeDisplay', ['com_foos.foo', &$item, &$item->params]);
$item->event->beforeDisplayContent = trim(implode("\n", $results));
$results = Factory::getApplication()->triggerEvent('onContentAfterDisplay', ['com_foos.foo', &$item, &$item->params]);
$item->event->afterDisplayContent = trim(implode("\n", $results));
return parent::display($tpl);
}
Are you wondering why we set &$item->params
as parameters for the event methods
onContentPrepare
,onContentAfterTitle
,onContentBeforeDisplay
andonContentAfterDisplay
,
although we have not yet explicitly implemented &$item->params
in the Foo extension? Implicitly, the populateState
method of the file /components/com_foos/src/Model/FooModel.php
ensures that &$item->params
is available. For our example, we do not need this third parameter so far. However, it is possible that errors may occur in combination with other extensions if this parameter is not set. Therefore, we set the three mandatory parameters ['com_foos.foo', &$item, &$item->params]
for all event methods.
Via
onContentAfterTitle
,onContentBeforeDisplay
,onContentAfterDisplay
, in addition to the custom fields, other elements are displayed that are mapped to the related event.
components/com_foos/tmpl/foo/default.php
IIn the template we display our custom fields. In our case, this is not complex, so we write all the stored texts one after the other. In a more complex file, the events are inserted at the correct place.
components/com_foos/tmpl/foo/default.php
}
echo $this->item->name;
echo $this->item->event->afterDisplayTitle;
echo $this->item->event->beforeDisplayContent;
echo $this->item->event->afterDisplayContent;
Note: Individual positioning
Custom fields can be positioned as desired in custom overrides.
Calling a field in an override
Basically, all custom fields that belong to the current item are available in the template components/com_foos/tmpl/foo/default.php
. The call is made via a property of the variable $this->item
with the name jcfields
. The variable $this->item->jcfields
is an array which contains for example the following data per field:
stdClass Object
(
[id] => 1
[alias] => nina
[publish_down] =>
[publish_up] =>
[published] => 0
[state] => 0
[catid] => 8
[access] => 1
[name] => Nina
[params] =>
[jcfields] => Array
(
[1] => stdClass Object
(
[id] => 1
[title] => Text Custom Field
[name] => text-custom-field
...
)
)
[event] => stdClass Object
(
[afterDisplayTitle] =>
[beforeDisplayContent] =>
Text Custom Field:
Custom Field Value
[afterDisplayContent] =>
)
)
Render the field individually
To render the field in the template components/com_foos/tmpl/foo/default.php
you can use FieldsHelper::render()
.
You can find the class
FieldsHelper
in the fileadministrator/components/com_fields/src/Helper/FieldsHelper.php
, if you want to have a closer look at the method itself.
A simple override
<?php foreach ($this->item->jcfields as $field) : ?>
<?php echo $field->label . ':' . $field->value; ?>
<?php endforeach ?>
Override using FieldsHelper
<?php JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php'); ?>
<?php foreach ($this->item->jcfields as $field) : ?>
<?php echo FieldsHelper::render($field->context, 'field.render', array('field' => $field)); ?>
<?php endforeach ?>
Load fields individually
To add individual fields to the content, first select specific names for the custom fields. This way it is possible to directly address the field in the override code of the file components/com_foos/tmpl/foo/default.php
using the field name. In the Joomla backend set the automatic display for the field to No
. This way you prevent it from being automatically displayed at one of the default positions. Add the following code at the beginning of the template file components/com_foos/tmpl/foo/default.php
or its override to use direct access by name to fields in the overrides.
<?php
foreach($item->jcfields as $jcfield) {
$item->jcFields[$jcfield->name] = $jcfield;
}
?>
Finally, add the insert statement for the custom field where you want the field label or value to appear.
<?php echo $item->jcFields['DEINFELDNAME']->label; ?>
<?php echo $item->jcFields['DEINFELDNAME']->value; ?>
You can find more information in the Joomla documentation[^docs.joomla.org/J3.x:Adding_custom_fields/Overrides/de]
Test your Joomla component
-
install your component in Joomla version 4 to test it: Copy the files in the
components
folder into thecomponents
folder of your Joomla 4 installation. A new installation is not necessary. Continue using the files from the previous part. -
open the view of your component in the administration area.
-
after that create a custom field of type 'text', if you didn't do it in the previous chapter.
-
edit a published foo item. Make sure you add a value to the custom field.
- at the end open the detail view of the just edited Foo item. Create a menu item for this. You will see next to the previously existing values now additionally the text you entered in the custom field.
Webmentions