Как в Laravel создать подключение к БД в runtime

Бывают ситуации, например, когда предопределённых в конфиге Laravel подключений к базам данных не достаточно и возникает потребность создавать подключения динамически во время выполнения PHP скрипта. Например, креденшиналы задаются как параметры консольной команды. Иногда это единственный способ проинтегрироваться с посторонним приложением. Далее рассмотрим один из способов решения подобных задач.

Для начала определим входные аргументы и опции с помощью новой модной нотации.

public function __construct() {
        $this->signature .= ' {host} {--port=3306} {--d|database=app} {--u|user=} {--p|password=} {--no-password} {--t|table=logs}';
        parent::__construct();
    }

И набросок кода для получения значений и создания нового подключения.

public function handle() {
        $connection = 'integration';
        $host = $this->argument('host');
        $port = $this->option('port');
        $database = $this->option('database');
        $user = $this->option('user');
        $password = $this->option('password');
        $no_password = $this->option('no-password');
        if($password && $no_password) {
            throw new \Exception('Please use only password or no-password option!');
        }
        if(null == $password && false == $no_password) {
            $password = $this->secret('What is the password?');
        }
        $table = $this->option('table');
        \Config::set("database.connections.{$connection}", [
            'driver'    => 'mysql',
            'host'      => $host,
            'port'      => $port,
            'database'  => $database,
            'username'  => $user,
            'password'  => $password,
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
        ]);
        $logs = \DB::connection($connection)->table($table)->where(/**/)->get();
    }

Т.к. передача пароля в открытом виде в команде это не секъюрно, то на случай передачи пустого пароля предусмотрен его интерактивный запрос.

Однако, если подключение к БД не защищено паролем, что ещё более не секъюрно, то предусмотрен костыль — опция no-password, для пропуска ввода пароля при его отсутствии. Фреймворк Laravel хоть и предоставляет средства для упрощения разработки консольных команд, но, возможности Symfony Console Component сильно ограничены и не покрывают всех кейсов, которые можно увидеть при работе с другими известными консольными приложениями.

Затем создаётся новая секция в конфиге подключений и сам запрос в базу данных. Стоит заметить, что метод Config::set() влияет только на рантайм, т.е. в файл конфига правки внесены не будут! Да, синтаксис весьма корявый: передача массива, ключи которого нужно подсматривать в документации, и определение секций в виде строки с точкой в роли разделителя. Но другого варианта Laravel не предоставляет.

Периодически встречаю вариацию такого подхода, где добавляется ещё одна опция, например, имя класса или метода, с помощью которого будут обработаны данные. Например:

--class=\App\Integration\Processors\AnotherIntegrationProcessor

Но выглядит это не читаемо + не юзер-френдли. На мой взгляд, более лучший вариант это создание отдельной команды (естественно, отнаследованной от абстрактной) под каждый вид обработчика. Преимущества данного метода: более короткий хвост опций у команды, актуальный список обработчиков доступен в списке команд.