Dynamic database connection in Silex with Doctrine 2 - php

I am trying to do something very similar to this post - Dynamic database connection symfony2 - but with Silex.
I have successfully setup my base database and the one I want to connect to dynamically.
database:
base:
driver: pdo_sqlite
path: database/dev.sqlite
website:
driver: pdo_sqlite
path: ~
The above is read into $dbs_options and then the following is used to configure this:
$app -> register(new DoctrineServiceProvider, ['dbs.options' => $dbs_options]);
// configure the ORM identities
$app -> register(new DoctrineOrmServiceProvider, [
'orm.proxies_dir' => Utils::joinPaths($app -> config -> appRoot, 'running', 'proxies'),
'orm.em.options' => [
'mappings' => $mappings
]
]
);
// set up multiple entity managers and assign the base connection as default
$app['orm.ems.default'] = 'basedb';
$app['orm.ems.options'] = [
'basedb' => [
'connection' => 'base',
'mappings' => $app['orm.em.options']['mappings']
],
'websitedb' => [
'connection' => 'website',
'mappings' => $app['orm.em.options']['mappings']
]
];
In my before event I am able to successfully interrogate the basedb to get the name of the website database that I want to connect to.
This is where I am stuck, I do not know and am not able to find how to reconfigure the database connection in Silex. Has anyone done this at all?

I did not have such a request in Silex, but you can always use
$conn = Doctrine\DBAL\DriverManager::getConnection($params, $config);
to create connection to database

Related

Symfony 5 Cannot autowire argument $Driver for a dynamic database connection

I am currently facing a problem, I explain myself. For my project I'm trying to make a dynamic connection to a database (I have 2 virtual machines with a different IP but with the same identifiers and tables with MSSQL database engine (SQLSRV)).
I try something like this ->
use Doctrine\DBAL\Driver\SQLSrv\Driver;
/**
* #Route("/testconnection", name="test_connect")
*/
public function testConnection(Driver $Driver){
$connectionParams = array(
'dbname' => 'job',
'user' => 'sa',
'password' => 'Lasernet#2020',
'host' => '192.168.1.34',
'driver' => 'pdo_sqlsrv',
);
$conn = $Driver->connect($connectionParams);
dd($conn);
}
Error message
Cannot autowire argument $Driver of "App\Controller\HomeController:testConnection()": it references class "Doctrine\DBAL\Driver\SQLSrv\Driver" but no such services exists.
Error message
But the problem is that Symfony sends me back an error that I find hard to solve/understand.
If someone has a solution to my problem/success to make a dynamic connection to databases.
If you need more information tell me.
The error message says it. No such service exists.
You must define the Doctrine\DBAL\Driver\SQLSrv\Driver class as a symfony service in your public/services.yaml to autowire it.
Update your public/services.yaml with:
services:
Doctrine\DBAL\Driver\SQLSrv\Driver:
autowire: true
But why you will autowire this class? The same behaviour you can get with
public function testConnection(){
$Driver = new Driver();
$connectionParams = array(
'dbname' => 'job',
'user' => 'sa',
'password' => 'Lasernet#2020',
'host' => '192.168.1.34',
'driver' => 'pdo_sqlsrv',
);
$conn = $Driver->connect($connectionParams);
dd($conn);
}
And if you have no really dynamic values in your connection params like $connectionParams['dbname'] = 'sqldb'.$i, better to use the doctrine dbal config and get the connection with $this->getDoctrine()->getConnection('name'); in your controller.
Symfony Docs

Botman conversation cache time not working

BotMan Version: 2.6
PHP Version: 7.3.23
Laravel Version : 7.16.1
Cache Driver: LaravelCache
i'm using botman for telegram bot.
everything is ok with botman just the conversation cache time is not working.
this is my botman Configuration code :
use BotMan\BotMan\Cache\LaravelCache;
use BotMan\BotMan\Drivers\DriverManager;
use BotMan\BotMan\BotManFactory;
$config = [
// Your driver-specific configuration
"botman" => [
'conversation_cache_time' => 720 ,
'user_cache_time' => 720,
],
"telegram" => [
"token" => env('TELEGRAM_TOKEN'),
]
];
// Load the driver(s) you want to use
DriverManager::loadDriver(\BotMan\Drivers\Telegram\TelegramDriver::class);
// Create an instance
$botman = BotManFactory::create($config, new LaravelCache());
// and other hears , fallback and conversations functions ...
every thing about the bot and conversations is fine , but the problem is about the conversation cash time
base on the conversation document we have to use drive cache to use conversations and the driver i'm using is laravelCache but i set conversation_cache_time to 720 minute but it just takes the default 30 minute.
what should i do?
thanks in advance.
From these lines in their github:
https://github.com/botman/botman/blob/79310f6e6464436aaa2d0522267b2ca00a07fda5/tests/BotManConversationTest.php#L79-L83
https://github.com/botman/botman/blob/4ec6e3f30d620cbcb73a0cf8e1dbf6b34e47f75d/src/Traits/HandlesConversations.php#L47
https://github.com/botman/botman/blob/203e7f5ef68473dd4d71ca7ee31275eae9a92745/src/BotMan.php#L238-L239
It must be like these:
$config = [
'user_cache_time' => 720,
'config' => [
'conversation_cache_time' => 720 ,
],
// Your driver-specific configuration
"telegram" => [
"token" => env('TELEGRAM_TOKEN'),
]
];
and it works.

Is it possible to do migration on dynamic database connection with Laravel 5?

I am trying to dynamically create database for different users. (every user will have their own database server, so don't ask why I am not using a single database for all users) To do that, I have a default database storing all the connection information. I will need to:
Create a new database and run all migration files on new user registration.
Run new migration files on all database recorded in this default database when there is update in schema.
Is there a way I can dynamically set the database connection of the migration file based on the information I have on the default database?
P.S. For "dynamically set the database connection", I am NOT meaning the normal setting as you do in controller or class. I expect something that would at least create migration table in the target database and be able to self-detect what migration file to run.
Yes there is. First you need to add the connection details to the configuration. Once you have a named connection configured, just call the migrate command on the Artisan facade, selecting the name of the connection ("new" in this example) as option:
use Illuminate\Support\Facades\Artisan;
//...
$new_connection = 'new';
config(["database.connections.$new_connection" => [
// fill with dynamic data:
"driver" => "mysql",
"host" => "",
"port" => "",
"database" => "",
"username" => "",
"password" => "",
"charset" => "utf8",
"collation" => "utf8_unicode_ci",
"prefix" => "",
"strict" => true,
"engine" => null
]]);
Artisan::call('migrate', ['--database' => $new_connection]);
First you need to create the database
DB::getConnection()->statement('CREATE DATABASE :schema', array('schema' => $schemaName));
Then change the name of the database on the fly like this
$config = app(\Illuminate\Config\Repository::class);
$config->set('database.connections.mysql.database', UserRepotory::getCurrentDatabase());
You can include Config like this or trough laravel's service container.
And finally you call Artisan::call('migrate')
Hi little help for you,
first of all add '%new_connection%' in database.php file to handle new connection for future use too.
To dynamically create connection, let say you have a route with variable $name for database name.
step 1:
in routes.file I have created and call it on your desired route url in routes.php
function appendNewConnection($name){
$path = base_path('config' . DIRECTORY_SEPARATOR . 'database.php');
$contents = file_get_contents($path);
$updatedContents = str_replace('%new_connection%', $name . '\' => [
\'driver\' => \'mysql\',
\'host\' => \'127.0.0.1\',
\'database\' => \'' . $name . '\',
\'username\' => \'root\',
\'password\' => \'\',
\'charset\' => \'utf8\',
\'collation\' => \'utf8_unicode_ci\',
\'prefix\' => \'\',
\'strict\' => false,
],
\'%new_connection%', $contents);
file_put_contents($path, $updatedContents);
}
Step 2:
//to generate migration add below line in top of routes.php
use Illuminate\Support\Facades\Artisan;
add this line in function created above
Artisan::call('migrate', ['--database' => $name]);
Here I have some clue regarding how can you do this:
1. There will be a global database where you are maintaining all users
login details, Right?
2. Add one extra field for database name.
3. when user logs in success full then store their database details in session
variable.
Now,
4. Create a database file dynamic and give database name from that session variable as:
config(["database.connections.$new_connection" => [
// fill with dynamic data:
"driver" => "mysql",
"host" => "",
"port" => "",
"database" => "",//Here you need to set value of session variable
"username" => "",// credential will be the same for all
"password" => "",// credential will be the same for all
"charset" => "utf8",
"collation" => "utf8_unicode_ci",
"prefix" => "",
"strict" => true,
"engine" => null
]]);
Bingo you now are ready to go :D

Yii2 Set component value dynamically

I am using this library https://github.com/yiioverflow/yii2-imap
'imap' => [
'class' => 'roopz\imap\Imap',
'connection' => [
'imapPath' => '{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX',
'imapLogin' => 'abc#gmail.com',//set this value dynamically
'imapPassword' => '123',//set this value dynamically
'serverEncoding' => 'encoding', // utf-8 default.
'attachmentsDir' => 'uploads/attachments'
],
],
//Create imap class object
$mailbox = yii::$app->imap->connection;
// Read all messaged into an array:
$mailsIds = $mailbox->searchMailbox('ALL');
in controller. want to set this value with help of session in yii2.
I found alternative php-imap library here [PHP IMAP][1]
[1]: https://github.com/barbushin/php-imap. which can be easily install with composer in yii2. and can pass a dynamic value
$mailbox = new PhpImap\Mailbox('{imap.gmail.com:993/imap/ssl}INBOX', 'some#gmail.com', '*********', __DIR__);
// Read all messaged into an array:
$mailsIds = $mailbox->searchMailbox('ALL');
Passing dynamic values that depends on Yii::$app right in config will not work because you are referring to application, and it's constructed using that config (components is a part of application too) and doesn't exist at this moment. It needs to be set later, when application is initialized and Yii::$app object exists. For example, in controller or some custom component.
Using library yiioverflow/yii2-imap it can be done like this:
use Yii;
...
Yii:$app->imap->connection = [
'imapPath' => '{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX',
'imapLogin' => $imapLogin, // Set this value dynamically
'imapPassword' => $imapPassword, // Set this value dynamically
'serverEncoding' => 'encoding', // utf-8 default
'attachmentsDir' => 'uploads/attachments',
],
Then you need to call:
Yii:$app->imap->createConnection();
to properly update the config.
There is no way to set separately imapLogin or imapPassword because of the way this component is written (these properties are protected and filled from connection array). If you want to do that, you have to subclass this component and write these setters by yourself and replace used component with your custom one.
More info about application components can be found in official docs.
You can use own "service layer" (that works similar to global Yii::$app). Just create \yii\di\ServiceLocator instance:
// Init service layer.
$services = new ServiceLocator();
$services->setComponents([
'imap' => [
'class' => 'roopz\imap\Imap',
'connection' => [
'imapPath' => '{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX',
'imapLogin' => 'abc#gmail.com',//set this value dynamically
'imapPassword' => '123',//set this value dynamically
'serverEncoding' => 'encoding', // utf-8 default.
'attachmentsDir' => 'uploads/attachments'
],
],
// ...
]);
// Retrieving the defined components:
$imap = $services->get('imap');
$imap = $services->imap;
If imap component will use only your controller, you can store $services as protected/private property of this controller.
Described approach works completely similarly to usual components in Yii::$app, because application class is also ServiceLocator.
Alternatively, you can define or redefine your component using imap-instance:
// Preparing components
$defaultImapConfig = [
'connection' => [
'imapPath' => '{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX',
'imapLogin' => null,
'imapPassword' => null,
'serverEncoding' => 'encoding', // utf-8 default.
'attachmentsDir' => 'uploads/attachments'
],
];
// Init service layer.
$services = new ServiceLocator();
// Define component
$imap = new \roopz\imap\Imap(ArrayHelper::merge($defaultImapConfig, ['connection' => [
'imapLogin' => 'abc#gmail.com',
'imapPassword' => '123',
]]));
$services->set('imap', $imap);
// Redefine component with new config
$imap = new \roopz\imap\Imap(ArrayHelper::merge($defaultImapConfig, ['connection' => [
'imapLogin' => 'dfg#gmail.com',
'imapPassword' => '456',
]]));
$services->set('imap', $imap); // If component definition with the same name already exist, it will be ovewritten.
Of course, you can use similar way to redefine global components in Yii::$app, but it is bad practice. I recommend to create separate (local) service layer, that can be accessed from your controllers, models etc.
More details about work with service locators you can found here.

Setting/retrieving mode in Slim Framework V3

I am new to Slim Framework (PHP). I was going through tutorials and testing some code and tried the following:
$app=new App([
'mode'=>file_get_contents(INC_ROOT . '/mode.php')
]);
echo $app->config('mode');
The output was blank.
It seems the function config() works with V2, however I am using V3. What am I doing wrong?
Slim 3 no longer has a config method. Instead, you must add configuration settings through the dependency injection container:
$app = new \Slim\App([
'settings' => [
'mode' => true
]
]);
$container = $app->getContainer();
echo $container->get('settings')['mode'];
A few things worth noting:
Slim 3 no longer handles managing different versions of configuration settings through a mode setting. So, you can set a value for a setting variable called mode as I've demonstrated here, but it won't actually do anything (i.e., Slim won't use it to determine your environment).
As an alternative, you can check out userfrosting/Config, a library we've been working on that can search multiple directories and different environment configuration files, merging together their contents:
/path/to/config/default.php
return [
'contacts' => [
'housekeeper' => [
'name' => 'Alex',
'email' => 'alex#cleansthetoilet.com'
]
]
];
/path/to/config/production.php
return [
'contacts' => [
'housekeeper' => [
'email' => 'alex#istheboss.com'
]
],
'database' => [
'password' => 'sup3rC-cr3t'
]
];
index.php
$app = new \Slim\App();
$container = $app->getContainer();
// Site config object (separate from Slim settings)
$container['config'] = function ($c) {
// Create and inject new config item
$config = new \UserFrosting\Config\Config();
$config->setPaths([
'/path/to/config'
]);
$config->loadConfigurationFiles('production');
return $config;
};
This will recursively merge in the settings from development.php with those in default.php, updating settings with the same name and scope as necessary:
Running print_r($container['config']); returns:
[
'contacts' => [
'housekeeper' => [
'name' => 'Alex',
'email' => 'alex#istheboss.com'
]
],
'database' => [
'password' => 'sup3rC-cr3t'
]
]
Notice that the value of contacts.housekeeper.email has been updated to 'alex#istheboss.com', and that the database config info has been merged in. Incidentally, you can also access config settings using the more convenient "dot syntax":
$config = $container->get('config');
echo $config['contacts.housekeeper.email'];
// Easier to type instead of $config['contacts']['housekeeper']['email'];
We recommend injecting this as a separate config service in Slim, rather than using their settings array.
You can combine this with phpdotenv to load settings from your system environment, or any .env files you create:
/path/to/config/production.php
return [
'database' => [
'password' => getenv('DB_PASSWORD')
]
];

Categories