1

I have a plugin which needs to change its parameter configuration depending on some conditions upon configuring in backend. The plugin using the SubscriberInterface because it is listening to some events.

This field value needs to change dynamicaly in the backend editing of the system plugin using it.

Here is a simple version:

class MyPlugin extends CMSPlugin implements SubscriberInterface
{
    protected $app;
    protected $autoloadLanguage = true;

    public static function getSubscribedEvents(): array
    {
        return [
            'onContentPrepareForm'  => 'onContentPrepareForm',
             // custom events following here
        ];
    }

    public function __construct(&$subject, $config)
    {
        parent::__construct($subject, $config);
    }

    public function onContentPrepareForm(Event $event)
    {
        $form = $event->getArgument('0');
        $data = $event->getArgument('1'); 

        // works
        $form->removeField('provider', 'params');

        // none of this works
        $data->provider = 'custom';  
        $form->setValue('provider', null, 'custom');
        $form->setValue('provider', '', 'custom');
        $form->setValue('provider', 'params', 'custom');
        $form->setValue('jform[params][provider]', '', 'custom');
    }

Here is the important piece of the xml

 <config>
        <fields name="params">
            <fieldset name="basic">
                <field name="provider" label="Provider" type="text" />

Update

I found out i can do this inside the onContentPrepareForm or onContentPrepareData function:

$data->params['provider'] = 'xxxxxxxxxx';

But this feels kind of weird. Maybe there is a better solution.

1 Answer 1

2

You're actually doing it as intended within Joomla. Here's a bit of background to explain why you're right, and for the sake of others who may be interested.

Overall the Joomla Form Basic API guide tries to explain how Joomla forms work. The first part of that page treats the subject at a fairly low level, and the second part considers how forms are handled within the Joomla MVC paradigm, which is what we have to deal with in practice.

Forms are loaded in the Model class of the component by calling a library function loadForm() and passing the filename of the XML file which contains the form.

So when you're setting plugin parameters within Joomla administrator you're in the com_plugins component, and its Model in administrator/components/com_plugins/src/Model/PluginModel.php will call loadForm() and pass the filename of the XML file in administrator/components/com_plugins/forms/plugin.xml. If you have a look at that file you'll see the similarity with the standard plugin options shown on the right hand side of the plugin form.

When you display a form on a web page sometimes you want to populate the form fields with data:

  • if you are editing a database record, then you want to prefill the fields with their current value in the database

  • if the user has entered some data into the form and then submitted the form, but the Joomla server code has found errors in the data, then when you re-present the form you want to prefill the fields with what he/she previously entered, so that it can be more easily corrected.

Hence the loadForm function has a parameter which specifies if prefill data should be loaded. If this is set to true then loadForm will call a callback function loadFormData which it will expect the component Model to provide. The com_plugins provides this function: it checks if there's previously entered data on the form, and if not it loads the existing plugin fields from the database record. It doesn't matter that there are more fields in this data object which is built from the database than there are fields in the form XML – Joomla will just take the data items it needs. But notice that to do this seamlessly the name of each field in the XML file matches the name of the equivalent database column.

Once it has built the data object the com_plugins function loadFormData calls a library function preprocessData, and it's this library function which triggers the 'onContentPrepareData' event whose purpose is to allow plugins to make any desired modification to this data.

The library loadForm function then calls the component model's preprocessForm function, and within PluginModel it gets your plugin's manifest XML file and injects the section of this file which is within the <config> tags into the Form object (which was built from administrator/components/com_plugins/forms/plugin.xml). The fields within the <config> tags provide the options which are specific to that individual plugin, and these are stored as name-value pairs in a JSON string in the params field of the database record. That's why you're right to have

 <config>
     <fields name="params">
        ...
            <field name="provider" label="Provider" type="text" />

as the data will get stored in the params field as a string {"provider": "xxx"}.

The library loadForm function then triggers the event 'onContentPrepareForm', passing the Form instance and the data object, to allow plugins once again to modify data values (or the Form itself).

It doesn't matter whether to choose to modify your data in 'onContentPrepareForm' or in 'onContentPrepareData'. Either way, you can set the data as you're doing:

$data->params['provider'] = 'xxxxxxxxxx';

Next the data object is bound to the Form instance via a bind() call. You can still modify data values after that, but it's a bit trickier, and you have to use the API methods described in the Form Advanced API guide.

Finally, just a couple things to be aware of.

If you're defining a plugin like this then the onContentPrepareForm and onContentPrepareData functions are going to get called whenever any form is displayed by Joomla, back-end or front-end. So you should always check that it's the form you're interested in by doing

if ($form->getName() === 'com_plugins.plugin') {…}

and also that it's just your own plugin that you're affecting:

if ($data->name === '<your plugin name>') {…}

Also the $data is held as a Joomla\CMS\Object\CMSObject instance, and you should really check that it is indeed that type of object before you go accessing the properties. I guess there is the chance that com_plugins could change this in the future to be a Joomla\Registry\Registry object, and then you'd need to change the getter and setter you use.

1
  • Thanks for the wide explanation.
    – maidan
    May 18, 2023 at 16:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.