How to retrieve the values from the dotenv file vars inside a Symfony 4 command

Recently, due to an issue with the PHP architecture of Xampp in Windows that is always of 32 bits, an script that used more than 2048MB of RAM, so it couldn't run on the browser. This forced me to download the 64 bits version of PHP and create a Symfony command for the logic that i was trying to implement on the browser. Unfortunately, a very vital of the script, specifically a single line, was driving me crazy because it was always empty for unknown reasons.

The problem was in trying to obtain the value of DATABASE_URL from my .env file using the getenv method of PHP:

// in the first versions of Symfony 4 you could retrieve it
// with getenv
getenv("DATABASE_URL");

// In most recent versions of Symfony 4 and 5
// You may retrieve this value through the global $_ENV 
$_ENV("DATABASE_URL");

In the controllers, it works pretty well, it used to return a string like:

DATABASE_URL=sqlite:///%kernel.project_dir%/var/data.db

However, inside the console command, it was always empty. As reported by many users, this seems to happen only in Apache servers, but in case that you are trying to implement a solution, this should be platform agnostic. The solution that i ended up using was simply to inject the .env parameters that i needed manually either in the command or creating a service that allows me to obtain parameters and then inject the service in my command. In this article, i will explain you how to proceed in both ways:

A. Inject single parameter 

As long as you want to pass just a couple of parameters into the command, you may simply register the database parameter and obtain it in the constructor, proceed to register the parameter in the services.yaml:

# config/services.yaml
services:
    App\Command\YourCommand:
        $databaseUrl: '%env(DATABASE_URL)%'

Consequently, retrieve the variable in the constructor of the command class, for example:

<?php

// src/Command/YourCommand.php
namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class YourCommand extends Command
{
    // the name of the command (the part after "bin/console")
    protected static $defaultName = 'app:do-something';

    private $databaseUrl;

    protected function configure($databaseUrl)
    {
        $this->databaseUrl = $databaseUrl;
        // ...
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // The database URL is: sqlite:///%kernel.project_dir%/var/data.db
        $output->writeln("The database URL is: " . $this->databaseUrl);

        return 0;
    }
}

And that should be enough for just a couple of parameters.

B. Create a service to obtain parameters from the services.yaml

As mentioned, the only way to obtain the parameters from the .env file in your command will be to register them in the services.yaml file. However, if you register many parameters in the yaml file, you may not want to inject every single variable in the class and retrieving them in the constructor, instead you may simply register the parameters in the yaml file:

# config/services.yaml
parameters:
    app.parameter1: '%env(SOME_PARAMETER1)%'
    app.parameter2: '%env(SOME_PARAMETER2)%'
    app.parameter3: '%env(SOME_PARAMETER3)%'
    app.parameter4: '%env(SOME_PARAMETER4)%'
    app.parameter5: '%env(SOME_PARAMETER5)%'
    app.parameter6: '%env(SOME_PARAMETER6)%'
    app.parameter7: '%env(SOME_PARAMETER7)%'

And then create a service that allows you to retrieve the defined parameters in the yaml file. We wrote a tutorial about how to create such service namely in this article, you can implement it in your project and then require it and obtain them like this:

<?php

// src/Command/YourCommand.php
namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

// Import helper
use App\Service\ContainerParametersHelper;

class YourCommand extends Command
{
    // the name of the command (the part after "bin/console")
    protected static $defaultName = 'app:do-something';

    private $pathHelpers;

    protected function configure(ContainerParametersHelper $pathHelpers)
    {
        $this->pathHelpers = $pathHelpers;
        // ...
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln("Parameter value: " . $this->pathHelpers->getParameter('app.parameter1'));
        $output->writeln("Parameter value: " . $this->pathHelpers->getParameter('app.parameter2'));
        $output->writeln("Parameter value: " . $this->pathHelpers->getParameter('app.parameter3'));
        $output->writeln("Parameter value: " . $this->pathHelpers->getParameter('app.parameter4'));
        $output->writeln("Parameter value: " . $this->pathHelpers->getParameter('app.parameter5'));
        $output->writeln("Parameter value: " . $this->pathHelpers->getParameter('app.parameter6'));
        $output->writeln("Parameter value: " . $this->pathHelpers->getParameter('app.parameter7'));

        return 0;
    }
}

And that's it ! You should only decide which solution to implement according to the quantity of parameters that you have and want to send to the command.

Happy coding !

This could interest you

Become a more social person