Skip to main content

Extending the Custom Field Edit Form

The onJ2CommerceCustomFieldFormPrepare event fires after the core custom field edit form loads. Use it to inject additional Joomla Form fieldsets — each fieldset becomes an extra tab in the edit interface alongside the built-in Basic, Display Settings, and Telephone Countries tabs.

When to Use​

Use this event when your plugin stores per-field configuration that belongs in the custom field admin form rather than in your own plugin settings. Examples:

  • A vendor management plugin adds a "Vendor Settings" tab with a field category selector (general / premium vendors only).
  • A file upload plugin adds an "Upload Settings" tab with allowed extensions and maximum file size fields.

Do not use this event just to display your area's enable/disable switcher. That is handled automatically by registering a display area.

Event Specification​

PropertyValue
Event nameonJ2CommerceCustomFieldFormPrepare
Dispatched byCustomfieldModel::getForm() after loading the base form
Argument formThe Joomla Form object — modify in place
Argument dataThe current form data (object or array)
ResponseNone — modify $form directly using $form->load()
When firedEvery time the custom field edit form is loaded (new and edit)

How the Edit Template Renders Plugin Fieldsets​

The custom field edit template iterates all fieldsets on the form. Core fieldsets (basic, telephone_countries, display) are rendered with dedicated markup. Any remaining fieldsets — including those your plugin injects — are rendered as additional tabs:

// From administrator/components/com_j2commerce/tmpl/customfield/edit.php

$coreFieldsets = ['basic', 'telephone_countries', 'display'];
$allFieldsets = $form->getFieldsets();

foreach ($allFieldsets as $fieldset) {
if (\in_array($fieldset->name, $coreFieldsets, true)) {
continue; // rendered separately above
}
echo HTMLHelper::_('uitab.addTab', 'myTab', $fieldset->name, Text::_($fieldset->label));
echo '<div class="row">';
foreach ($form->getFieldset($fieldset->name) as $field) {
echo '<div class="col-md-6">' . $field->renderField() . '</div>';
}
echo '</div>';
echo HTMLHelper::_('uitab.endTab');
}

Your fieldset's label attribute must be a language key.

Complete Working Example​

Plugin Handler​

<?php
// File: plugins/j2commerce/app_myplugin/src/Extension/MyPlugin.php

declare(strict_types=1);

namespace Acme\Plugin\J2commerce\App_myplugin\Extension;

\defined('_JEXEC') or die;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\Event;
use Joomla\Event\SubscriberInterface;

final class MyPlugin extends CMSPlugin implements SubscriberInterface
{
public $autoloadLanguage = true;

public static function getSubscribedEvents(): array
{
return [
'onJ2CommerceCustomFieldFormPrepare' => 'onCustomFieldFormPrepare',
];
}

public function onCustomFieldFormPrepare(Event $event): void
{
$form = $event->getArgument('form');
// $data = $event->getArgument('data'); // available if you need current values

$xml = '<form>
<fieldset name="myplugin_settings"
label="PLG_J2COMMERCE_APP_MYPLUGIN_FIELDSET_SETTINGS">
<field name="myplugin_field_category"
type="list"
label="PLG_J2COMMERCE_APP_MYPLUGIN_FIELD_CATEGORY"
description="PLG_J2COMMERCE_APP_MYPLUGIN_FIELD_CATEGORY_DESC"
default="">
<option value="">COM_J2COMMERCE_SELECT</option>
<option value="general">PLG_J2COMMERCE_APP_MYPLUGIN_CATEGORY_GENERAL</option>
<option value="premium">PLG_J2COMMERCE_APP_MYPLUGIN_CATEGORY_PREMIUM</option>
</field>
<field name="myplugin_required_for_checkout"
type="radio"
label="PLG_J2COMMERCE_APP_MYPLUGIN_FIELD_REQUIRED_CHECKOUT"
description="PLG_J2COMMERCE_APP_MYPLUGIN_FIELD_REQUIRED_CHECKOUT_DESC"
layout="joomla.form.field.radio.switcher"
filter="integer"
default="0">
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
</form>';

$form->load(new \SimpleXMLElement($xml));
}
}

Language Strings​

; File: plugins/j2commerce/app_myplugin/language/en-GB/plg_j2commerce_app_myplugin.ini

PLG_J2COMMERCE_APP_MYPLUGIN_FIELDSET_SETTINGS="My Plugin Settings"
PLG_J2COMMERCE_APP_MYPLUGIN_FIELD_CATEGORY="Field Category"
PLG_J2COMMERCE_APP_MYPLUGIN_FIELD_CATEGORY_DESC="Assign this field to a display category within the plugin."
PLG_J2COMMERCE_APP_MYPLUGIN_CATEGORY_GENERAL="General"
PLG_J2COMMERCE_APP_MYPLUGIN_CATEGORY_PREMIUM="Premium Vendors Only"
PLG_J2COMMERCE_APP_MYPLUGIN_FIELD_REQUIRED_CHECKOUT="Required for Checkout"
PLG_J2COMMERCE_APP_MYPLUGIN_FIELD_REQUIRED_CHECKOUT_DESC="Force this field to be required when used in the standard checkout."

Using $form->load() with SimpleXMLElement​

$form->load() accepts a SimpleXMLElement whose root is <form>. Fieldsets inside it merge with the existing form — fields are added, not replaced. If a field name already exists in a fieldset with the same name, the existing field takes precedence.

Key rules for the XML:

  • The root element must be <form>.
  • Each <fieldset> must have a name attribute. Avoid names that match core fieldsets (basic, telephone_countries, display).
  • The label attribute on <fieldset> must be a language key. The edit template calls Text::_() on it.
  • Field type attributes follow standard Joomla form field types. Custom types require addfieldprefix on the <form> element.
// Using a custom field type in an injected fieldset
$xml = '<form addfieldprefix="Acme\Plugin\J2commerce\App_myplugin\Field">
<fieldset name="myplugin_settings" label="PLG_J2COMMERCE_APP_MYPLUGIN_FIELDSET_SETTINGS">
<field name="myplugin_color" type="ColorPicker"
label="PLG_J2COMMERCE_APP_MYPLUGIN_FIELD_COLOR" />
</fieldset>
</form>';

$form->load(new \SimpleXMLElement($xml));

Persisting Plugin Fieldset Values​

The core model saves all form fields that match columns in #__j2commerce_customfields. Plugin-injected fields whose names do not match real columns are silently discarded by parent::save().

To persist plugin fieldset values, intercept the save flow from your own plugin controller or store the values in a separate table keyed by j2commerce_customfield_id. A common pattern is to serialize the plugin's fields into the params column if one exists on your table, similar to how #__j2commerce_addresses uses params.

Best Practices​

  • Prefix all field names with your plugin identifier: myplugin_field_category, not just field_category. Field names in a Joomla form share a flat namespace.
  • Use a name on your <fieldset> that is also prefixed: myplugin_settings. This prevents conflicts with other plugins injecting fieldsets.
  • Keep injected fieldsets focused — one tab per plugin is the expected pattern. Multiple unrelated tabs degrade the admin UX.
  • The event fires on every load of the edit form. Do not perform expensive operations (database queries, HTTP requests) inside the handler.