1

I'm looking for a way to run the DB "Fix" action that was present in J3 from the Manage Extensions Database menu item.

Longer explanation -- I am trying to deploy upgrades from my dev environment to production using git. After deploying the code updates via git, I can't get into the Joomla admin backend because tables are missing related to the upgrade. I need a way to trigger the DB upgrade from CLI, or if there's some magic URL that will work.

The CLI commands for core:check-updates and core:update don't do anything because it already recognizes the J4 code that is in place.

2 Answers 2

1

The Update Structure button on the Maintenance:Database enter image description here

appears to execute the fix() method at line #40 of this file ..\administrator\components\com_installer\src\Controller\DatabaseController.php which in turn uses the fix() method of the model at ..\administrator\components\com_installer\src\Model\DatabaseModel.php

You could create your own console plugin with some basic code to identify the Joomla instance for the cid and then it should follow the normal core processing for checking the database as part of installation of updates.

Alternatively you could set up a command line instance of Joomla to trigger the core processing. This post explains how to set up the required environment and you will need add the code to start the core processing. How to create an instance of the Joomla CMS from the browser or the command line

6
  • Unfortunately, I found that the "Fix" action only addresses structural discrepancies in the DB. It doesn't actually run the upgrade steps. I ended up having to manually apply all of the upgrade SQL files (there were over 60, so it was a pain). Jan 16 at 14:06
  • The doDatabaseTransactions() method of ..\libraries\src\Installer\InstallerAdapter.php appears to be the entry point for processing the SQL files during an extensions installation that you maybe able to hook into. Otherwise maybe the extension:discover and extension:discover:install console commands could be useful to reprocess the 'installation' after the files have been updated by Git. You may have to remove your extension from the #__extensions table first to get Discover to work or at least switch the state in the same table.
    – Irata
    Jan 16 at 21:17
  • I'm not working with an extension. I'm trying to replay the core Joomla DB upgrade process after deploying code via git. The reality is that multiple aspects of Joomla make it very difficult to work with in a git workflow. The absence of any way to trigger the DB upgrade step is just one (likely the most significant) of the hurdles. Jan 17 at 1:17
  • Git is obviously only handling the text based files and what you are trying to do is clone the DB table structure and data at that same point in time which would be more suited in a SQL tool to sync or clone your Joomla DB.
    – Irata
    Jan 17 at 1:37
  • No, I'm not trying to clone the DB. I'm trying to replay the DB Upgrade steps that would be triggered if I upgraded through the UI. Both Wordpress and Drupal provide the ability to run the DB upgrade step without dependence on a UI-based upgrade process. Joomla apparently does not, which makes it very difficult to use a git-based deployment process. Jan 17 at 12:42
0

If I understand you correctly, I think you're looking for a way of processing a file which contains Joomla SQL commands. If that's the case then you can write a console job to do that.

Here's a plugin which will do that in a basic way - it doesn't contain database transaction control though, which you might want. It's based on Writing_A_CLI_Application

Into a folder plg_dbupgrade_cli put the following files:

Manifest file: plg_dbupgrade_cli/dbupgrade_cli.xml:

<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="console" method="upgrade">
    <name>Execute SQL file console command</name>
    <version>1.0.0</version>
    <creationDate>today</creationDate>
    <author>Mycompany</author>
    <description>Executes a file of SQL commands (eg for an upgrade)</description>
    <namespace path="src">Mycompany\Plugin\Console\Dbupgrade</namespace>
    <files>
        <folder plugin="dbupgrade_cli">services</folder>
        <folder>src</folder>
    </files>
</extension>

plg_dbupgrade_cli/services/provider.php (just boilerplate code to get an instance of the plugin via the dependency injection container).

<?php
defined('_JEXEC') or die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Mycompany\Plugin\Console\Dbupgrade\Extension\DbupgradeConsolePlugin;

return new class implements ServiceProviderInterface
{
    /**
     * Registers the service provider with a DI container.
     *
     * @param   Container  $container  The DI container.
     *
     * @return  void
     *
     * @since   4.2.0
     */
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container) {
                $dispatcher = $container->get(DispatcherInterface::class);
                $plugin     = new DbupgradeConsolePlugin(
                    $dispatcher,
                    (array) PluginHelper::getPlugin('console', 'dbupgrade_cli')
                );

                return $plugin;
            }
        );
    }
};

plg_dbupgrade_cli/src/Extension/DbupgradeConsolePlugin.php (standard code for creating a console command)

<?php
namespace Mycompany\Plugin\Console\Dbupgrade\Extension;

\defined('_JEXEC') or die;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Joomla\Application\ApplicationEvents;
use Joomla\CMS\Factory;
use Mycompany\Plugin\Console\Dbupgrade\CliCommand\RunDbupgradeCommand;

class DbupgradeConsolePlugin extends CMSPlugin implements SubscriberInterface
{
    protected $autoloadLanguage = true;
    
    public static function getSubscribedEvents(): array
    {
        return [
            \Joomla\Application\ApplicationEvents::BEFORE_EXECUTE => 'registerCommands',
        ];
    }

    public function registerCommands(): void
    {
        $app = Factory::getApplication();
        $app->addCommand(new RunDbupgradeCommand());
    }
}

plg_dbupgrade_cli/src/CliCommand/RunDbupgradeCommand.php (based on Installer::parseSQLFiles code in libraries/src/Installer/Installer.php)

<?php
namespace Mycompany\Plugin\Console\Dbupgrade\CliCommand;

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\Database\DatabaseInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Input\InputOption;
use Joomla\Console\Command\AbstractCommand;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Installer\Installer;

class RunDbupgradeCommand extends AbstractCommand
{
    /**
     * The default command name
     *
     * @var    string
     * @since  4.0.0
     */
    protected static $defaultName = 'sql:execute-file';

    /**
     * Internal function to execute the command.
     *
     * @param   InputInterface   $input   The input to inject into the command.
     * @param   OutputInterface  $output  The output to inject into the command.
     *
     * @return  integer  The command exit code
     *
     * @since   4.0.0
     */
    protected function doExecute(InputInterface $input, OutputInterface $output): int
    {
        $symfonyStyle = new SymfonyStyle($input, $output);

        $symfonyStyle->title('Run a SQL file');
        
        $sqlfile = $input->getOption('sqlfile', 'Please enter a file of joomla sql commands');
        if (!file_exists($sqlfile)) {
            $symfonyStyle->error("{$sqlfile} does not exist");
            return false;
        }
        
        $buffer = file_get_contents($sqlfile);
        if ($buffer === false) {
            $symfonyStyle->error("Could not read contents of {$sqlfile}");
            return false;
        }
        
        $installer = Installer::getInstance();
        $queries = $installer::splitSql($buffer);
        if (\count($queries) === 0) {
            $symfonyStyle->error("No SQL queries found in {$sqlfile}");
            return false;
        }
        $symfonyStyle->info(\count($queries) . " SQL queries found in {$sqlfile}");
        
        $db = Factory::getContainer()->get(DatabaseInterface::class);
        foreach ($queries as $query) {

            try {
                $symfonyStyle->info("Executing: \n{$query}");
                $db->setQuery($query)->execute();
                $symfonyStyle->success(Text::_('Success'));
            } catch (ExecutionFailureException $e) {
                $symfonyStyle->warning($e->getMessage());
                return false;
            }
        }

        return 0;
    }

    /**
     * Configure the command.
     *
     * @return  void
     *
     * @since   4.0.0
     */
    protected function configure(): void
    {
        $this->addOption('sqlfile', null, InputOption::VALUE_OPTIONAL, 'file of joomla sql commands', "");
        $this->setDescription('Run a list of SQL commands in a file');
        $this->setHelp(
            <<<EOF
The <info>%command.name%</info> command runs the SQL commands in the file passed as the --sqlfile argument
<info>php %command.full_name%</info>
EOF
        );
    }
}

When you install and enable the plugin you should then be able to do a CLI command:

php cli/joomla.php sql:execute-file --sqlfile=test.sql

I think the update files might be in administrator/components/com_admin/sql.

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.