CakePHP3 Changing Database Connections in Plugins - php

I have problems with switching a database connection in CakePHP3.
I would like to change the database connection (based on a subdomain, MultiTenant system). But I would like to outsource this connection-change to a plugin.
For this I wrote a small plugin (MTA) with a middleware class, with the following invoke function:
public function __invoke($request, $response, $next)
{
$response = $next($request, $response);
$tenantMap = TableRegistry::get('TenantMappings');
$mapping = $tenantMap->findByName($request->subdomains()[0])->firstOrFail();
ConnectionManager::config("alias_".$request->subdomains()[0], [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => '***username***',
'password' => '***password***',
'database' => '***database***',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
]);
ConnectionManager::alias ("alias_".$request->subdomains()[0], "default");
Configure::write('Account.active', $mapping->account_id);
return $response;
}
That does not work. Maybe it is already too late to change the connection or set up an alias.
Is there still a possibility to change the connections for all models in a plugin, or do I have to do this earlier (for example in bootstrap.php)?

That should work fine for all tables that are being instantiated after that point, however you need to move the $next() call (which invokes the next middlware in the queue) to the end of the method, otherwise your code will run after the application request has been completed.

Related

Be able to change database connection in runtime with Laravel

So I am making a web application which should be able to upload any SQL statement to any server listed in the database on a schedule. It should connect to the database, execute the statement and make a report based on the information received. Then it can disconnect.
Currently, I have a job in place and it grabs all the information required from the database eg. host, port, DBName etc. I just need to form a connection and then execute the statement.
I am using the Laravel framework.
Edit: I cant modify any database configs as the database information is stored in a table, not a config file.
Thanks
You can set it at runtime this way:
$connKey = 'CustomConnection';
$dbInfo = DatabaseInfo::find($databaseId);
Config::set('database.connections.' . $connKey, array(
'driver' => 'mysql',
'host' => $dbInfo->hostName,
'database' => $dbInfo->database,
'username' => $dbInfo->username,
'password' => $dbInfo->password,
'charset' => 'utf8',
'collation' => 'utf8_general_ci',
'prefix' => '',
));
// Now you can set the new connection to the models
$myModel = new MyModel;
$myModel->setConnection($connKey);
// Or you can use it this way too:
$users = DB::connection($connKey)->select(...);
// Or, also:
$pdo = DB::connection($connKey)->getPdo();
I solved this in a very similar way to one posted :
$connectionName = Crypt::encryptString(str_random(80));
DB::purge("$connectionName");
Config::set("database.connections.$connectionName", [
"driver" => "$Driver",
"port" => "$Port",
"host" => "$Host",
"database" => "$DBName",
"username" => "$UserName",
"password" => "$Password"
]);
$query = DB::connection("$connectionName")->select($statement);
$query= json_decode( json_encode($query), true);
Excel::create("$filename-$mytime", function($excel) use ($query, $filename) {
$excel->sheet("$filename", function($sheet) use ($query){
$sheet->fromArray($query);
});
})->store('xls', storage_path('excel/exports'));
This creates a new connection with a random name (so when in a queue if multiple are executed they wont purge the same database), assigns the details to it, executes the select statement which is defined in the variable $statement. Then creates an excel spreadsheet from that.

Different redis databases for cache and sessions in Laravel 4.2

I am trying to move away from file cache and use redis instead. I can get it working using the same redis database for cache and sessions but this means I cannot clear the application cache without losing all sessions so I am wanting to run the two on different databases on the same server. My configs are as follows:
database.php
'redis' => array(
'cluster' => false,
'default' => array('host' => 'redisserverip', 'port' => 6379, 'database' => 0),
'session' => array('host' => 'redisserverip', 'port' => 6379, 'database' => 1),
),
cache.php
'driver' => 'redis',
'connection' => null,
session.php
'driver' => 'redis',
'connection' => 'session',
This is not working as both the application cache and sessions are being saved into the 1st database when it should be shared accross the 0th and 1st database. Is this a bug in Laravel or a problem with my config?
I dont think its a bug, but the options for making a connection to a Redis server is never being introduced as I can see in Redis Class for Laravel 4.2
protected function createSingleClients(array $servers)
{
$clients = array();
foreach ($servers as $key => $server)
{
$clients[$key] = new Client($server);
}
return $clients;
}
While if you compare it with the Redis Class for Laravel 5 , the options for each server is passed so the feature is supported.
protected function createSingleClients(array $servers)
{
$clients = array();
$options = $this->getClientOptions($servers);
foreach ($servers as $key => $server)
{
$clients[$key] = new Client($server, $options);
}
return $clients;
}
I am taking into consideration, that in your example you are looking for the No cluster option.
The connection parameter has nothing to do with redis, as stated on the config file cache.php
When using the "database" cache driver you may specify the
connection that should be used to store the cached items. When this
option is null the default database connection will be utilized for
cache.

ZF2 Zend\Paginator\Adapter\DbSelect on DB2 i Series

Im creating a Zend APIgility application REST service and having a problem with my fetchAll Mapper function.
I'm connecting to an IBM DB2 database running on an i Series server (AS/400) via DB2 Connect on a Windows Application Server.
My Connection is is made in my local.php as such:
return array(
'db' => array(
'driver' => 'IbmDb2',
'database' => $database,
'username' => $user,
'password' => $password,
'hostname' => $host,
'port' => $port,
'driver_options' => array(
'i5_naming' => DB2_I5_NAMING_ON,
'i5_lib' => 'LIBWEB',
),
),
);
The fetchAll() function in my Mapper class is:
public function fetchAll()
{
$select = new Select('WBRESOURCE');
$paginatorAdapter = new DbSelect($select, $this->adapter);
$collection = new ResourcesCollection($paginatorAdapter);
return $collection;
}
When I hit the DbSelect, ZF2 throws the following DB2 Connect error:
"[IBM][CLI Driver][AS] SQL0204N \"*LIBL.WBRESOURCE\" is an undefined name. SQLSTATE=42704"
Im not sure why its using *LIBL (user defined library list), since I defined the library (SCHEMA) to use as LIBWEB in my connection option.
Thanks in advance!
Rob
Try changing this section:
'driver_options' => array(
'i5_naming' => DB2_I5_NAMING_ON,
'i5_lib' => 'LIBWEB',
Change to":
'driver_options' => array(
'i5_naming' => DB2_I5_NAMING_OFF, <=== change
'i5_lib' => 'LIBWEB',
By using DB2_I5_NAMING_OFF, you should get SQL naming mode. The use of DB2 i5 naming mode will result in things like reliance on a job's library list.
See PHP: db2-connect for some info on the parameter.

Using different databases based on user request

I've installed Laravel-4 and jenssegers / Laravel-MongoDB which has the same interface to Eloquent model as Laravel, so everything is pretty transparent and 1 database connection works OK.
what I'm trying to do, is switch to another database based on user request (Consider it as API that decided where to go and grab data).
This is what I did:
App::before(function($request)
{
$dbPrefix = $request->segment(1);
if (!is_null($dbPrefix)) {
$dbName = strtolower($dbPrefix);
$newDb = DB::connection('mongodb_'.$dbName);
}
});
From here.. I don't know what to do.. Is it connected to new database that way? how do I tell my Laravel to use $newDb when I refer to DB constant in Models?
But I want it to happen before application starts, so specifying "$connection" variable in model or using explicit call to other database like DB::connection('mongodb2')->query(...) is no good for me.
Thanks
The solution to this would be:
/app/filters.php:
App::before(function($request)
{
$dbPrefix = $request->segment(1);
if (!is_null($dbPrefix)) {
$connectionName = 'mongodb_'.strtolower($dbPrefix);
Config::set('database.default', $connectionName);
}
});
/config/database.php:
'mongodb_soccer' => array(
'driver' => 'mongodb',
'host' => 'localhost',
'port' => 27017,
'username' => 'user',
'password' => 'password',
'database' => 'SoccerData'
),
'mongodb_tennis' => array(
'driver' => 'mongodb',
'host' => 'localhost',
'port' => 27017,
'username' => 'user',
'password' => 'password',
'database' => 'TennisData'
)
Requests:
site.com/soccer
will get connection mongodb_soccer
site.com/tennis
will get connection mongodb_tennis
You can pre-authorize it in default "admin" database where your users are stored, and then switch to any database connection per user request to get the actual data.
I needed it this way for API development.
Good luck

Getting symfony query and result cache to work against oracle 11g

I'm trying to get the memcache query/result cache working with Oracle. It works flawlessly against mysql (verified with memcached console: ./memcached -u nobody -m 40 -vv). Here's what's in web/index.php:
$servers = array(
'host' => 'localhost',
'port' => 11211,
'persistent' => false
);
$cacheDriver = new Doctrine_Cache_Memcache(array(
'servers' => $servers,
'compression' => false,
'prefix' => 'qc-')
);
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine::ATTR_QUERY_CACHE, $cacheDriver);
$manager->setAttribute(Doctrine::ATTR_RESULT_CACHE, $cacheDriver);
$manager->setAttribute(Doctrine_Core::ATTR_RESULT_CACHE_LIFESPAN, 3600);
This works as supposed against MySQL but fails with following message at the first location where i use ->useResultCache(true):
Result Cache driver not initialized.
Does anyone have clue about what's happening and/or if there's additional configuration needed to get it working with Oracle DB backend?
Thanks.
you should try to set those manager attributes inside your YOUR_APPConfiguration class like explained here : http://www.funstaff.ch/2009/03/19/le-cache-de-resultat-avec-doctrine
class frontendConfiguration extends sfApplicationConfiguration
{
public function configureDoctrine(Doctrine_Manager $manager)
{
$servers = array(
'host' => 'localhost',
'port' => 11211,
'persistent' => false
);
$cacheDriver = new Doctrine_Cache_Memcache(array(
'servers' => $servers,
'compression' => false,
'prefix' => 'qc-')
);
$manager->setAttribute(Doctrine_Core::ATTR_QUERY_CACHE, $cacheDriver);
$manager->setAttribute(Doctrine_Core::ATTR_QUERY_CACHE_LIFESPAN, 3600);
$manager->setAttribute(Doctrine_Core::ATTR_RESULT_CACHE, $cacheDriver);
$manager->setAttribute(Doctrine_Core::ATTR_RESULT_CACHE_LIFESPAN, 3600);
}
}

Categories