Implementing custom fields is relatively simple. For starters, your
component extension must implement the
Joomla\CMS\Fields\FieldsServiceInterface
.
The two methods defined in the interface are implemented a lot like this
in most cases:
public function validateSection($section, $item = null) { if (!in_array($section, ['categories', 'item'])) { return null; } return $section; } public function getContexts(): array { Factory::getApplication()->getLanguage()->load('com_example', JPATH_ADMINISTRATOR); return [ 'com_example.item' => Text::_('COM_EXAMPLE_TITLE_ITEMS'), 'com_example.categories' => Text::_('JCATEGORY'), ]; }
The former method makes sure that a context's section (the stuff
after the dot) is a valid section where you expect custom fields to exist.
In this example we expect two sections, categories
and
item
.
The latter method returns a list of contexts for our component where
custom fields can be defined. We defined the two contexts corresponding to
the sections we accept in the validateSection
method. The return values will be used by com_fields
to
render the drop-down which lets the user select the section for which they
define custom fields for your component.
Your XML manifest must, of course, include two links to the Joomla
Fields component, one for the Fields and one for the Fields group. Use one
of the contexts you returned in the getContexts
method above. Do NOT use the context for categories.
<menu link="option=com_fields&view=fields&context=com_example.item"> JGLOBAL_FIELDS <params> <menu-quicktask><![CDATA[index.php?option=com_fields&view=field&layout=edit&context=com_example.item]]></menu-quicktask> <menu-quicktask-title>COM_EXAMPLE_SUBMENU_FIELDS_NEW</menu-quicktask-title> <menu-quicktask-permission>core.create;com_fields</menu-quicktask-permission> </params> </menu> <menu link="option=com_fields&view=groups&context=com_example.item"> JGLOBAL_FIELD_GROUPS <params> <menu-quicktask><![CDATA[index.php?option=com_fields&view=group&layout=edit&context=com_example.item]]></menu-quicktask> <menu-quicktask-title>COM_EXAMPLE_SUBMENU_FIELD_GROUPS_NEW</menu-quicktask-title> <menu-quicktask-permission>core.create;com_fields</menu-quicktask-permission> </params> </menu>
Your backend item edit pages should display the custom fields automatically — as long as you render all of the form's fieldset without looking for specific names. For example, something similar to this code:
<?php use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; ?> <form action="<?= Route::_('index.php?option=com_example&view=item&layout=edit&id=' . $this->item->id) ?>" aria-label="<?= Text::_('COM_ATS_TITLE_TICKETS_EDIT', true) ?>" class="form-validate" id="adminForm" method="post" name="adminForm" > <input name="task" type="hidden" value=""> <?= HTMLHelper::_('form.token') ?> <?php foreach ($this->form->getFieldsets() as $fieldSet): ?> <div class="card mb-2"> <h3 class="card-header bg-info text-white"> <?= Text::_($this->form->getFieldsets()[$fieldSet]->label) ?> </h3> <div class="card-body"> <?php echo $this->form->renderFieldset($fieldSet); ?> </div> </div> <?php endforeach; ?> </form>
Joomla handles loading and saving the values of custom fields automatically as long as the Content - Fields plugin is published.
In the frontend, fields' contents are returned through the
onContentAfterTitle
,
onContentAfterDisplay
and
onContentBeforeDisplay
events. Typically, the
implementation in your HtmlView class looks similar to this:
<?php use Joomla\CMS\Factory; use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; use Joomla\CMS\Plugin\PluginHelper; class HtmlView extends BaseHtmlView { /** * The item object details * * @var \Joomla\CMS\Object\CMSObject * * @since 1.6 */ protected $item; // ... your code here ... /** * Execute and display a template script. * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void|boolean */ public function display($tpl = null) { $app = Factory::getApplication(); $state = $this->get('State'); $item = $this->get('Item'); $this->form = $this->get('Form'); // ... your code here ... // Process the content plugins. PluginHelper::importPlugin('content'); $offset = $state->get('list.offset'); // Some plugins require a text attribute without checking if it exists $item->text = ''; $app->triggerEvent('onContentPrepare', [ 'com_example.item', &$item, &$item->params, $offset ]); // Store the events for later $item->event = new \stdClass(); $results = $app->triggerEvent('onContentAfterTitle', [ 'com_example.item', &$item, &$item->params, $offset, ]); $item->event->afterDisplayTitle = trim(implode("\n", $results)); $results = $app->triggerEvent('onContentBeforeDisplay', [ 'com_example.item', &$item, &$item->params, $offset, ]); $item->event->beforeDisplayContent = trim(implode("\n", $results)); $results = $app->triggerEvent('onContentAfterDisplay', [ 'com_example.item', &$item, &$item->params, $offset, ]); $item->event->afterDisplayContent = trim(implode("\n", $results)); $this->item = $item; // ... your code here ... } }
Your view template can then output
$this->event->afterDisplayTitle
etc in appropriate
places.
![]() | Tip |
---|---|
If you want to see how you can override Joomla's default custom
field display to format it in a way that's more pleasant to the eye you
can download Akeeba Ticket System Core (it's free of charge) and look at
I have a method called
The two layout files
( It goes without saying, my clients can of course choose to override these layout files OR the entire view template to render custom fields the way they see fit. For example, a site integrator might choose to have a custom field which is never displayed automatically, get its value using the field name and display it in a visually pleasant display in the view template. For instance, someone could have a custom field plugin which saves geographical coordinates. These could be displayed in the frontend of the site as an OpenStreetMap map. With custom fields, sky's the limit! |