CodeIgniter Multiple database use without changing existing code for first database - codeigniter

I have successfully configured a second database with codeigniter and was able to create a table. The problem is now that every previously existing lines of code that was using $this->db now uses the second database so naturally it pops an error. Let me explain with a bit more details:
I use the migration library of codeigniter and in a new migration, I create the database with dbforge like so $this->dbforge->create_database('website_store'). So far so good, then I connect to this database using $DB2 = $this->load->database('store', TRUE); and create a table. All is good here as well.
Then the migration ends and codeigniter tries to update the migration table to push the latest version, but looks into the store database instead of the default database, so it pops this error:
Table 'website_store.migrations' doesn't exist
UPDATE `migrations` SET `version` = 45
Hopefully someone knows about using multiple database and the migration library. By the way, in the database.php file, it is set to use the default database: $active_group = "default";, not the store database so it should work. It looks like the migration library sees that there is already a database loaded, feels lazy and uses that one instead.
NOTE:
Looks like these guys had similar problems that they fixed by setting 'pconnect' to 'false', but it would be nice to keep a connection to both database:
Convenient Way to Load Multiple Databases in Code Igniter
Error Using Multiple Database In CodeIgniter

In CI i have dealt with multiple databases using:
function readFromOtherDB(){
$config = array(
'hostname' => "XXX",
'username' => "XXX",
'password' => "XXX",
'database' => "XXX",
'dbdriver' => "mysql",
'dbprefix' => "",
'pconnect' => FALSE,
'db_debug' => TRUE,
'cache_on' => FALSE,
'cachedir' => "",
'char_set' => "utf8",
'dbcollat' => "utf8_general_ci"
);
$otherDb = $this->ci->load->database($config, true);
$query = $otherDb->get_where('users', array('email' => $_email));
$otherDb->close();
}

Related

Query parameter binding issue with illuminate/database and illuminate/queue

I'm using Illuminate\Queue outside of a Laravel app inside an add-on for a CMS. So the only instances of Laravel or Illuminate are these packages that I've required:
"illuminate/queue": "^8.83",
"illuminate/bus": "^8.83",
"illuminate/contracts": "^8.83"
I'm first trying to use the Database for the queue as the default driver since the CMS is database driven, then provide options to SQS etc. I've setup everything so the migrations create my queue tables and everything seems to be wired together when I make the following call to push something to the queue.
/** #var \Illuminate\Queue\QueueManager $queue */
$queue->push('test', ['foo' => 'bar']);
Then it ends in the following error. The parameter bindings are not working or something. It's leaving the ? in the values list.
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"exp_dgq_jobs" ("queue", "attempts", "reserved_at", "available_at", "created_at"' at line 1 (SQL: insert into "exp_dgq_jobs" ("queue", "attempts", "reserved_at", "available_at", "created_at", "payload") values (default, 0, ?, 1674567590, 1674567590, {"uuid":"6bf7a17e-dda3-4fed-903a-8714e5a2d146","displayName":"test","job":"test","maxTries":null,"maxExceptions":null,"failOnTimeout":false,"backoff":null,"timeout":null,"data":{"foo":"bar"}}))
I've step debugged the whole request and it feels like a bug, but then again this is really the first time I've used Laravel or one of it's packages, so maybe I'm missing something? This function explicitly sets reserved_at to null, and the Connection->prepareBindings() method doesn't do anything with the ?, it just leaves it as that value, so the query fails.
protected function buildDatabaseRecord($queue, $payload, $availableAt, $attempts = 0)
{
return ['queue' => $queue, 'attempts' => $attempts, 'reserved_at' => null, 'available_at' => $availableAt, 'created_at' => $this->currentTime(), 'payload' => $payload];
}
What am I missing? Everything just looks right to me an I'm kind of at a loss. I'm making this with PHP 7.4 in mind (for the time being). Maybe I'll try 8.1 to see if that changes anything with the Illuminate packages. Using MySQL 8 too.
Update: potentially relevant screenshot just before the error.
Update 2: I tried PHP 8.1 and latest Laravel 9 packages, didn't make a difference.
For more clarity on how I"m creating my QueueManager:
<?php $queue = new Queue;
$queue->addConnection([
'driver' => 'database',
'table' => ee('db')->dbprefix . 'dgq_jobs',
'queue' => 'default',
'retry_after' => 90,
'after_commit' => false,
]);
$databaseConfig = $provider->make('DatabaseConfig');
$queue->addConnector('database', function () use ($databaseConfig) {
$pdo = new PDO(
sprintf('mysql:host=%s; dbname=%s', $databaseConfig['host'], $databaseConfig['database']),
$databaseConfig['username'],
$databaseConfig['password']
);
$connection = new Connection($pdo);
$connectionResolver = new ConnectionResolver(['default' => $connection]);
$connectionResolver->setDefaultConnection('default');
return new DatabaseConnector($connectionResolver);
});
return $queue->getQueueManager();
I was able to reproduce the error you were seeing. I haven't looked too deeply but I think it may be due to the PDO object not setting up the connection exactly as the Illuminate Queue library expects.
This modification to using the Illuminate\Database library to create the connection solved the issue in my test environment:
$database = new \Illuminate\Database\Capsule\Manager;
$queue = new \Illuminate\Queue\Capsule\Manager;
$database->addConnection([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'db_name',
'username' => 'username',
'password' => '',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
]);
$queue->addConnector('database', function () use ($database) {
$connection = $database->getConnection();
$connectionResolver = new \Illuminate\Database\ConnectionResolver(['default' => $connection]);
$connectionResolver->setDefaultConnection('default');
return new \Illuminate\Queue\Connectors\DatabaseConnector($connectionResolver);
});
$queue->addConnection([
'driver' => 'database',
'table' => 'jobs_table',
'queue' => 'default',
'retry_after' => 90,
'after_commit' => false,
]);
$queue->getQueueManager()->push('SendEmail', ['message' => 'test']);

Any recommended PHP package for redisSearch?

I thought, surely there must be php developers out there who use redisSearch. I have only seen two packages for this RedisSearch-php by Ethan Hann and php-redisearch by MCFJA. They return empty documents and php-redisearch by MCFJA is not beneficial since it uses a Predis client (not really ideal for large applications in production).
Please is there any Laravel/PHP developer who is using redissearch and making progress. I'd be hugely appreciative of any advice and help. Thanks.
$redis = new \Predis\Client([
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
]);
$builder = new \MacFJA\RediSearch\Index\Builder($redis);
// Field can be create in advance
$address = new \MacFJA\RediSearch\Index\Builder\GeoField('address');
$builder
->withName('person')
->addField($address)
// Or field can be create "inline"
->addTextField('lastname', false, null, null, true)
->addTextField('firstname')
->addNumericField('age')
->create();
$index = new \MacFJA\RediSearch\Index('person', $redis);
$index->addDocumentFromArray([
'firstname' => 'Joe',
'lastname' => 'Doe',
'age' => 30,
'address' => '40.689247,-74.044502'
]);
$search = new \MacFJA\RediSearch\Search($redis);
$results = $search
->withIndex('person')
->withQuery('Doe')
->withHighlight(['lastname'])
->withScores()
->search();
return $results; // returning empty arrays
The version 2.0.0 of macfja/redisearch is finally out.
This version have (built-in) support for multiple Redis provider.
It should have the most common ones, and adding a new one is pretty simple.
And for the empty document list, maybe it's cause by the inverted coordinate (should be [longitude],[latitude])

Call Migrations in Laravel 5.4 Controller

I'm creating a SAAS application with multi tenant databases. So, whenever a person registers on the site. I'm creating a database on run time and then after a connection on the fly. I want to run the migrations to create tables in the new database.
Everything is working fine but the migrations are not happening.
Here is my code which calls my migration:
Artisan::call('migrate', array('--path' => 'database/migrations', '--force' => true));
I'm already creating the migrations tables before calling this command.
Try with this:
Artisan::call('migrate',
[
'--database' => 'tenant',
'--path' => 'database/migrations',
'--step' => true,
'--force' => true
]);
Replace tenant with your database connection name.

Dynamic env-files (multiple databases) and artisan commands

I have a large project which will have each customer on their own separate database. To get this to work we use a custom .env-loader that loads each customers .envby checking the customers subdomain (unique to each customer).
However, of course this doesn't work with artisan commands. For instance, when I want to migrate, I will need to migrate all databases at once. So I've set up an Artisan command that fetches the .env-files and loop through them and then calls the default artisan migrate. But it is not working as expected.
I've tried everything; for instance:
$dotenv = new Dotenv('/env', '.test.env');
$dotenv->overload();
And:
app()->useEnvironmentPath('/env');
app()->loadEnvironmentFrom('.test.env');
And even:
config('database.connections.mysql.database', 'test_database');
As soon as I run $this->call('migrate'); the app defaults to the default .env and ignores all customizations at runtime. Does anyone have an idea on how I can overload the migration commands choice of database?
Note: I know that I can manually setup multiple connections in config/database.php (for instance like: Overriding Default Laravel database configuration for artisan migrate commands), however, image a few dozen customers and this would not be viable.
I had to do something similar with SQLite database that were being created by the console commands, and the only way I could get the migrations to run was by creating a database config on the fly:
Config::set('database.connections.'.$config_key, array(
'driver' => 'sqlite',
'database' => storage_path($database_name),
'prefix' => '',
));
And then I would call the migrate command:
Artisan::call('migrate', [
'--database' => $config_key,
'--path' => 'database/offline/'.$type.'/migrations',
]);
After a whole lot of issues I was able to sort it this way;
In Laravel 5 there seem to be a difference in Config::set(), config('config',['key' => 'value]) and config()-set('config', ['key' => 'value']).
After a lot of testing different variant we managed to get a solution this way;
$connection = 'connection';
$iterator = 0;
foreach ($files as $file) {
App::useEnvironmentPath('/env');
App::loadEnvironmentFrom('.file.env');
// Create a new connection "on the fly"
config()->set('database.connections.' . $connection . '_' . $iterator, [
'driver' => 'mysql',
'host' => env('DB_HOST'),
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
]);
// Call regular migration command
$this->call('migrate', ['--force' => true, '--database' => $connection . '_' . $iterator]);
$iterator++;
}
This manages to set multiple new connections to the MySQL-database, and then seed each one of them.
Thanks to #David Allen here for the inspiration.

Creating the database on fly

I am working on laravel 4 app, In that i wan to allow the user to register for the app and when they register the app should allow a new empty database to the registered user,I am using mysql for backend I know the following code to set the database connection
Config::set('database.connections', ConnectionArray);
And to set it as default
Config::set('database.default', ConnectionKey);
To connect another database manually i can use this way
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'database',
'username' => 'root',
'password' => '123456',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
'mysql_tenant1' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'tenant1',
'username' => 'root',
'password' => '123456',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
But i want the app should create database dynamically...
Creating the Database itself will be relatively simple. The complexity comes in associating those databases to users somehow, and then pulling those database names and using them in connections. This should help:
In terms of overall strategy, consider creating a new repository with a few functions:
RegisterUser() - This registers the user with username/password.
CreateDatabase($name , $user_id) - This should create your
database and store it's name in a table, and then assign it to the User ID you created in Register User.
You can run raw SQL queries in Laravel with DB::statement() (i.e - ``DB::statement('create database' . $name);`, so stick that in your CreateDatabase function. I tested this locally and it works just fine.
NOTE: You'll want to do some validation to make sure the database doesn't already exist, or your user is going to (obviously) get an error.
Then, you can pull the Database name from the table by User ID and create connections with it as described in the below SO!
Laravel 4: Multiple Tenant Application, each tenant it's own database and one global database
One thing - when you make a change to your database structure, you're going to have to update each database individually. Note that Artisan allows for this with the --database="database_name" option.
Just have a user_id field in whatever table(DB from what I think you're describing) you want, and use relationships to make it work, a whole bunch of fields in one table(or more than one table) may be FAR more efficient than what you think.

Resources