2

When a component is installed, it seems that until the user updates the configuration (using the "Options" button) a request to get the component parameters from Joomla returns an essentially empty object. The request:

$params = Joomla\CMS\Component\ComponentHelper::getParams('com_example);

results in a $params object that does not give values for $params->get('parmname'). It seems that the XML (config.xml) is not used during installation to create default parameters, but is only used when the user updates the "options".

Unfortunately, creating a new Registry object and using the method loadFile on config.xml does not produce a usable parameters object either. So a component that relies on parameter values (defaults will do until the user chooses something else) does not work on first installation.

Have I misunderstood something? Is there a solution to this?

4 Answers 4

3

It does seem that nobody has a solution to this issue based on a method within Joomla. The use of code in the script file that duplicates in different form the XML of config.xml is a work around. That is OK so far as it goes but it is clearly not ideal to have the same information in two different places. Especially in two different formats. It is almost certain to lead to inconsistencies sooner or later.

It is possible in a relatively few lines of code to turn the config.xml file into a Registry object, similar to the one that would be obtained from "getParams". It's here written as static methods within a utility class. Once you have a Registry object, you can obtain the JSON to store in the extension table by simply coercing the object to a string by putting (string) in front of it.

use Joomla\Registry\Registry;

public static function getParamsFromConfig($element) {
    $params = new Registry();
    $xmldata = simplexml_load_file(JPATH_ADMINISTRATOR . '/components/' . $element . '/config.xml');
    $paramarray = array();
    self::addLayerToParams($paramarray, $xmldata);
    $params->loadString(json_encode($paramarray));
    return $params;
}

private static function addLayerToParams(&$paramarray, $xmldata) {
    if ($xmldata->count()) {
        foreach ($xmldata->children() as $child) {
            self::addLayerToParams($paramarray, $child);
        }
    }
    if ('field' == $xmldata->getName()) {
        $paramarray[(string) $xmldata['name']] = (string) $xmldata['default'];
    }
}

I guess there must be code somewhat similar to this somewhere in com_config but so far as I can see, it is not readily accessible.

So far, I haven't explored the related question of how to deal with the situation where a new release of the component changes config.xml.

The code could be improved by adding error handling in case of mistakes in the XML of config.xml.

1

I too have come up against this problem and I can see three possible workarounds. Firstly and most simply ensure that whenever you are getting parameters using

$params = ComponentHelper::getParams('com_nonexistant',false) and then $params->get() be sure to include a default option:

$params->get('somevalue', 'fallbackDefault');

The "false" second parameter to the getParams() is the default but include it to be specific, if true then the getParams() would return false and "get()" would return an error. This does, of course, mean that you are having to enter your default values in two (or more) places - in the options.xml and in the php.

The second, is to include in the install script a prominent button after install (or discover or update) to the Options page for the extension with a prompt to check the options and save them before proceeding. Not infallible, but it might help.

button created by the postflight script The button links to https://mysite.uk/administrator/index.php?option=com_config&view=component&component=com_mycomponent

Thirdly is to use the technique shown above by mbrampton and mickmacusa, but rather than reading from a separate json why not simply read and parse the component xml file which will be available at /administrator/components/mycomponent/config.xml by the time postflight executes. Then use the default values read to save as shown above

That way you don't have to maintain a separate json.

1
  • A great, comprehensive answer, Roger. More of this generosity please. Do take our tour when you have a minute.
    – mickmackusa
    Jan 24, 2022 at 11:53
0

The key thing in the code in the first answer that doesn't want to display is this:

if ($type === 'install')
    {
        // Install the global extenstion params.
        $db    = JFactory::getDbo();
        $query = $db->getQuery(true);
        // Field to update.
        $fields = array(
            $db->quoteName('params') . ' = ' . $db->quote('{"authorName":"Your Name","authorEmail":"Your email","your config.xml param":"your config.xml param value"}'),
        );
        // Condition.
        $conditions = array(
            $db->quoteName('element') . ' = ' . $db->quote('com_mycom')
        );
        $query->update($db->quoteName('#__extensions'))->set($fields)->where($conditions);
        $db->setQuery($query);
        $allDone = $db->execute();

    }
0

It looks like your installer script is missing a line to store the global extension params. Is this Joomla 3 or 4?

----- Adding to answer

Here's an example of code from one of my components built using Joomla Component Builder.

Assuming your component is called mycom In your mycom.xml file add

<scriptfile>script.php</scriptfile>

add a file script.php to your install folder

<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
class com_mycomInstallerScript
{
    /**
     * Constructor
     *
     * @param   JAdapterInstance  $parent  The object responsible for running this script
     */
     public function __construct(JAdapterInstance $parent)
     {
     }
    /**
     * Called on installation
     *
     * @param   JAdapterInstance  $parent  The object responsible for running this script
     *
     * @return  boolean  True on success
     */
    public function install(JAdapterInstance $parent)
    {
    }

    /**
     * Called on uninstallation
     *
     * @param   JAdapterInstance  $parent  The object responsible for running this script
     */
    public function uninstall(JAdapterInstance $parent)
    {

    }

    /**
     * Called on update
     *
     * @param   JAdapterInstance  $parent  The object responsible for running this script
     *
     * @return  boolean  True on success
     */
    public function update(JAdapterInstance $parent)
    {
    }

    /**
     * Called before any type of action
     *
     * @param   string            $type    Which action is happening (install|uninstall|discover_install|update)
     * @param   JAdapterInstance  $parent  The object responsible for running this script
     *
     * @return  boolean  True on success
     */
    public function preflight($type, JAdapterInstance $parent)
    {
    }

    /**
     * Called after any type of action
     *
     * @param   string            $type    Which action is happening (install|uninstall|discover_install|update)
     * @param   JAdapterInstance  $parent  The object responsible for running this script
     *
     * @return  boolean  True on success
     */
    public function postflight($type, JAdapterInstance $parent)
    {
        // get application
        $app = JFactory::getApplication();

        // set the default component settings
        if ($type === 'install')
        {
            // Install the global extenstion params.
            $db    = JFactory::getDbo();
            $query = $db->getQuery(true);
            // Field to update.
            $fields = array(
                $db->quoteName('params') . ' = ' . $db->quote('{"authorName":"Your Name","authorEmail":"Your email","your config.xml param":"your config.xml param value"}'),
            );
            // Condition.
            $conditions = array(
                $db->quoteName('element') . ' = ' . $db->quote('com_mycom')
            );
            $query->update($db->quoteName('#__extensions'))->set($fields)->where($conditions);
            $db->setQuery($query);
            $allDone = $db->execute();

        }
        // do any updates needed
        if ($type === 'update')
        {
        }

        return true;
    }

}
3
  • Thanks, but what would that line consist of exactly? Either 3.9 or 4. Both seem to behave the same.
    – mbrampton
    May 3, 2021 at 16:38
  • Thanks for developing that. I can see that it would work, but it has the drawback of requiring a JSON string that duplicates (in a different form) information that is in config.xml. The latter is still needed, as it provides structure for the parameters. With the same information in two places, maintenance is harder and there is a risk of inconsistency. So, although a possibility, it isn't an ideal solution, I'm afraid. The fact that it is in Component Builder seems to confirm that there is a real issue.
    – mbrampton
    May 5, 2021 at 14:31
  • I'd agree, it looks like as standard Joomla doesn't install the params and Llewellyn has coded this work around in JCB.
    – mfleeson
    May 5, 2021 at 16:58

Your Answer

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

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