Using the Magento 2 setup tool with a non-default MySQL port

Using the Magento 2 setup tool with a non-default MySQL port

I dockerized my local development environment a few weeks ago and, in the process, split all my MySQL databases into separate Docker containers, which I start as needed and stop again when the work is done.

For a few days now I have been working on porting my tool n98-magerun to Magento 2. When I launched the setup tool, I was surprised that it was not possible to define a different database TCP port. In my case, all Magento databases run on 127.0.0.1, port 33310. A look at the source code quickly brought me back down to earth, though. A different port does not appear to be supported at present.

The process is as follows...

In step 2, you enter the database credentials and can have them validated via XHR. Behind the setup tool is an AngularJS-based Zend Framework 2 app, which also has to be installed separately via Composer.

magento2_angular_installer_step2

Pressing the button sends a POST request to "/setup/data", which accepts and validates the database credentials.

host: "127.0.0.1:33310"
name: "magento2"
useAccess: 1
useExistingDb: 1
user: "root"

In my case, I wanted appending a colon and the port number "33310" to the hostname "127.0.0.1" to be processed correctly.

The POST data is then processed through the factory \Magento\Setup\Module\Setup\ConnectionFactory. This is not really a factory yet and always returns a MySQL DB adapter. However, this is where the database credentials are processed.

The factory uses constants from the configuration class \Magento\Setup\Module\Setup\Config. To keep the code consistent, I added a new constant, KEY_DB_PORT, to the class.

/**
 * Deployment configuration model
 */
class Config
{
    /**#@+
     * Possible variables of the deployment configuration
     */
    const KEY_DATE    = 'date';
    const KEY_DB_HOST = 'db_host';
    const KEY_DB_PORT = 'db_port'; // <-- Eingefügte Konstante
    const KEY_DB_NAME = 'db_name';
    const KEY_DB_USER = 'db_user';
    const KEY_DB_PASS = 'db_pass';

    // ....
}

Now a small piece of code can be added to the factory to handle the port.

The class \Magento\Setup\Module\Setup\ConnectionFactory then looks like this:

class ConnectionFactory
{
    /**
     * Create DB adapter object
     *
     * @param \ArrayObject|array $config
     * @return \Magento\Setup\Framework\DB\Adapter\Pdo\Mysql
     */
    public function create($config)
    {
        $config[Config::KEY_DB_PORT] = 3306;
        if (strstr($config[Config::KEY_DB_HOST], ':')) {
            list($config[Config::KEY_DB_HOST], $config[CONFIG::KEY_DB_PORT]) = explode(':', $config[Config::KEY_DB_HOST]);
        }

        return new Mysql(
            [
                'driver' => 'Pdo',
                'dsn' => "mysql:dbname=" . $config[Config::KEY_DB_NAME] . ";host=" . $config[Config::KEY_DB_HOST] . ";port=" . $config['db_port'],
                'username' => $config[Config::KEY_DB_USER],
                'password' => isset($config[Config::KEY_DB_PASS]) ? $config[Config::KEY_DB_PASS] : null,
                'driver_options' => [\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'"]
            ]
        );
    }
}

After this, step 6, which runs the setup scripts, works as well, since it uses the same ConnectionFactory.

magneto2_angular_installer_step6

In conclusion, the new setup code is quick to understand. I think it would be better to introduce a separate field for the port. Alternatively, I could send my code to Magento so it can be included in the new installer.