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