Multiple databases in Laravel PHP [duplicate] - php

This question already has answers here:
How to use multiple databases in Laravel
(7 answers)
Closed 5 years ago.
We are building a multi-tenant application. Through the admin interface, we will add new tenant as and when required. This application needs to work with 1+n database.
1 Main DB with about 5 tables.
n DBs for each tenant that we create. The tenant specific database may reside on the separate db server altogether.
Question:
What is the best way to achieve this ?
Where do we store the the db connection information for each tenant ?
Sometime, we may have to fire join queries on tables in tenant and main db.
How would this work?
Thanks in advance for reading and any possible solution please.

Google is your friend, so is the documentation:
https://laravel.com/docs/5.4/database#using-multiple-database-connections
config/database.php
And add a new connection for each database-connection you want to use.
To switch connections:
$users = DB::connection('foo')->select(...);

We can set the DB connection in config/database.php in connections part:
'connections' => [
//Our primary DB
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
],
//Secondary DB
'mysql2' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB2_DATABASE', 'secondary'),
'username' => env('DB2_USERNAME', 'forge'),
'password' => env('DB2_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
],
],
And for Model we can add
class User2 extends Model
{
protected $connection = 'mysql2';
}
And in migration we can use connection method.
Schema::connection('mysql2')->create('table')
And if you use Eloquent ORM we can use setConnection('mysql2') on it.
$user = new User2;
$user->setConnection('mysql2');
This answer based on this question

In my case a wont use multi-tenant
store child db configurations in main database
set child db params in middleware
use $connection = 'child' in needed models
join is not possible, because may be different servers, but huge part of relations is work propertly (not whereExists, withCount.. etc)
Hope it`s help

Related

Laravel : How to set database connection pool?

I need to connect many databases dynamically in laravel app.
How to set database connection pool?
for example,there are many second-class domain name,like this:
chicago.example.com
newyork.example.com
losangeles.example.com
...
They have separate database:
chicago
newyork
losangeles
...
I connect these databases dynamically like this:
public function store(Request $request)
{
//post request from http://chicago.example.com/articles
$server_name_arr=explode('.',$_SERVER['SERVER_NAME']); //the result is ['chicago','example','com']
$db=array_slice($server_name_arr,-3,1)[0]; //the result is 'chicago'
Config::set('database.connections.mysql.database', $db);
DB::reconnect('mysql');
//...
}
For performance,I want to set database connection pool,how to do it in laravel?
If you have quite many different instances of your code and is difficult to manage the configuration (env file), you can modify your code, adding a condition to modify env file and cache the configuration if needed. This if you just need an admin functionality, it will not work if you have one instance, please look the code below.
If you have one instance and one DB server, then perhaps you can use table prefix to distinguish your tables, I mean to copy all your tables with a different prefix depending your subdomain.
But if you really need database coonection pool, there are some solutions for Laravel at Github like this , but I have never tried one.
public function store(Request $request)
{
//post request from http://chicago.example.com/articles
$server_name_arr=explode('.',$_SERVER['SERVER_NAME']); //the result is ['chicago','example','com']
$db=array_slice($server_name_arr,-3,1)[0]; //the result is 'chicago'
$current_db = DB::connection()->getDatabaseName();
if($db != $current_db){
$path = base_path('.env');
file_put_contents($path, str_replace('DB_DATABASE=' . current_db,
'DB_DATABASE=' . $db, file_get_contents($path)));
Artisan::call('config:cache');
}
//...
}
You can define multiple Mysql connections like this in database.php
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
],
'mysql2' => [
'driver' => env('DB_CONNECTION_SECOND'),
'host' => env('DB_HOST_SECOND'),
'port' => env('DB_PORT_SECOND'),
'database' => env('DB_DATABASE_SECOND'),
'username' => env('DB_USERNAME_SECOND'),
'password' => env('DB_PASSWORD_SECOND'),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
],
Define variables for same in .env file. Now create model with properties as below.
protected $connection = 'mysql2';
protected $table = 'tablename';
Model will refer to that particular table of remote database.

Laravel - Multi Site/Project

I'm currently looking for a framework to support my own new framework. Laravel seems to be my best choice.
I have a very specific structure in mind. My project will have a base design with common models, views and controllers, as well as some sub projects with there specific views. The goal is to provide a platform for owners of the same business, where we provide a unique website. In many cases only the design varies, whilst the structure and components stay the same. My idea was to create a maintainable structure on laravel wich every sites pulls its models, controllers and views from and if there is a specific need an additional view can be created.
Has somebody had experience with a simular project in the past?
I see some major obstacles:
Creating sub projects
Using multi database connections
Using models and views from the main project
project setup
Laravel is very flexible and highly configurable, you should have no problem doing things like that at all. As for the database, for example, you can create two connections: main, a fixed connection to your main database tables, and project for the current project tables, here's what it should look like:
'main' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
'sslmode' => 'prefer',
],
'project' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
'sslmode' => 'prefer',
],
And you should be able to:
Configure model connection:
<?php
namespace App;
class Posts extends Model
{
protected $connection = 'project';
}
Query connections directly:
DB::connection('project')->table('users')->where('activated', true)->get();
Configure the database in run time:
config([
'database.connections.project.database' => 'project1db',
'database.connections.project.user' => $user,
'database.connections.project.password' => $password,
]);
As for the views, you can tell Laravel to find views wherever you need by simply doing:
View::addLocation('/path/to/project1/');

Laravel: connect to databases dynamically

I'm creating an application in Laravel 5(.1) where it is needed to connect to different databases. The only problem is that it's not known which databases it has to connect to, so making use of the database.php in config is not possible. A controller is in charge of making a connection with dynamically given connection details.
How can I make a new connection to a database, including making use of the DB class? (Or is this possible)
Thanks in advance!
The simplest solution is to set your database config at runtime. Laravel might expect these settings to be loaded from the config/database.php file, but that doesn't mean you can't set or change them later on.
The config loaded from config/database.php is stored as database in Laravel config. Meaning, the connections array inside config/database.php is stored at database.connections.
So you can easily override/change these connections like this:
Config::set("database.connections.mysql", [
"host" => "...",
"database" => "...",
"username" => "...",
"password" => "..."
]);
From there on out, any Eloquent models that use this mysql connection will be using this new database connection config.
I'd recommend doing this in a Service Provider if possible.
I've stumbled upon the same problem.
You can actually change database settings in runtime and use them.
Use the config() function to set extra or overwrite existing connection settings.
config(['database.connections.mynewconnection' => {settings here}]);
Keep in mind that these settings are cached. So when you need to use the new settings, purge the DB cache for the connection you're gonna use.
DB::purge('mynewconnection');
You can also manipulate the default connection that is used. This can come in handy if you wish to use migrations over different connections and keep track of them with a migration table within the used connection. Or other cool stuff ofcourse...
DB::setDefaultConnection('mynewconnection');
You might need to use these:
use Illuminate\Support\Facades\Config;
use DB;
Set database configurations:
Config::set("database.connections.mysql_external", [
'driver' => 'mysql',
"host" => "localhost",
"database" => "db_name",
"username" => "root",
"password" => "root",
"port" => '8889',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
]);
Connect to database and do stuff:
$users = DB::connection('mysql_external')->select('Select id from users');
Disconnect database and reset config variables
DB::disconnect('mysql_external');
Config::set("database.connections.mysql_external", [
'driver' => 'mysql',
"host" => "localhost",
"database" => "",
"username" => "",
"password" => "",
"port" => '',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
]);
I ran into this problem too with a script that imports multiple MS Access DB files into MySQL and I was not satisfied with any of the solutions which suggested editing configuration at runtime. It was ugly and messy to dynamically create a new config for every single Access DB file that I wanted to import. After some playing, I manged to persuade Laravel to switch DBs on the existing connection like this:
$mysqlConn = DB::connection();
$mysqlConn->getPdo()->exec("USE $schemaName;");
$mysqlConn->setDatabaseName($schemaName);
//appServiceProvider.php
Connection::macro('useDatabase', function (string $databaseName) {
$this->getPdo()->exec("USE `$databaseName`;");
$this->setDatabaseName($databaseName);
});
//usage.
\DB::connection()->useDatabase($dbName);
Create a new database connection in your databse.php
'connections' => [
'new_db_connection' => [
'driver' => 'sqlsrv',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE_NEW', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
],
'old_db_connection' => [
'driver' => 'sqlsrv',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE_OLD', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
],
]
Create helper method
static function getDatabaseName()
{
// Apply your condition and return databse
if(url('/') === 'http://localhost:8001'){
return 'new_db_connection';
} else {
return 'old_db_connection';
}
}
Using query builder
$databaseName = Helper::getDatabaseName();
DB::connection($databaseName)
->table('your table name')
->select('*')
->get();
Using model
<?php
namespace App\Models;
use App\Helper;
use Illuminate\Database\Eloquent\Model;
class Test extends Model {
public function __construct()
{
// You can apply the below variable dynamically and model
// will use that new connection
$this->connection = Helper::getDatabaseName();
}
protected $table = "users";
}
// Without Using any Facades
config(['database.connections.mynewconnection' => [
'driver' => 'sqlsrv',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE_OLD', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
]]);
// With Facades
// Use Illuminate\Support\Facades\Config;
Config::set('database.connections.mynewconnection', [
'driver' => 'sqlsrv',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE_OLD', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
]);
Access the database using Query Builder
DB::connection('mynewconnection')->table(<table_name>)->get();
we can do it another way
class SomeModel extends Eloquent {
if(app::environment('local'))
{
protected $connection = 'mysql2';
}
now extend use SomeModel class instead of Model everywhere.
Reference : https://fideloper.com/laravel-multiple-database-connections

Using multiple databases for different endpoints

I have API written in lumen(laravel). I am using Eloquent for my models.
What I need to do is to use different databases based on url (endpoint).
For example I have http://apiprovider.com/api/v1/ as base API url and it connects to the api_v1 database, but I need to use another database if v2 is used http://apiprovider.com/api/v2 for instance api_v2 database.
All classes and laravel application should be the same, only different database according to version.
Database settings is stored in .env file.
Please suggest the right way to implement this ? Or at least possible ways.
Thanks.
It's just an idea, i haven't tried it but a simple way would be switching between the two databases from a middleware.
For example you could define the two connections available in database.php :
'connections' => [
'mysql1' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
'mysql2' => [
'driver' => 'mysql',
'host' => env('DB_HOST_2', 'localhost'),
'database' => env('DB_DATABASE_2', 'forge'),
'username' => env('DB_USERNAME_2', 'forge'),
'password' => env('DB_PASSWORD_2', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
]
Then create a middleware and switch the DB in the handle method
public function handle($request, Closure $next)
{
//check the request URL and decide what DB to use
//set the DB for this request
Config::set('database.default', $dbname );
}
I think that Config::set('database.default', $dbname); will work only for the current request, but it would do what you need

Best way to connect multiple databases in Laravel [duplicate]

This question already has answers here:
How to use multiple databases in Laravel
(7 answers)
Closed 5 years ago.
I am creating a multi-tenant application in which, based on the sub-domain, I am connecting to a database of that particular tenant.
Here is code to do that:
// To connect with a subdomain - the entry will be in config/database.php.
public static function connectSubdomainDatabase($dbname)
{
$res = DB::select("show databases like '{$dbname}'");
if (count($res) == 0) {
App::abort(404);
}
Config::set('database.connections.subdomain.database', $dbname);
//If you want to use query builder without having to specify the connection
Config::set('database.default', 'subdomain');
DB::reconnect('subdomain');
}
Is it the best way to connect with a database or is there any problem that because I am thinking from the performance point of view because every time I am connecting with the database when there are different subdomains. What is the best possible way to do that?
This is nearly the best way to do this. In the end, it's all opinion anyway. However, I would create a connection in the configuration file for each of the subdomains. Then, in your connectSubdomainDatabase() function, I would get the current subdomain instead of passing a database name. You can already specify a connection in laravel, the only place you should be using database names is in the config file.
So, something like this:
// To connect with a subdomain - the entry will be in config/database.php.
public static function connectSubdomainDatabase()
{
// Break apart host
$urlParts = explode('.', $_SERVER['HTTP_HOST']);
// Change default connection
Config::set('database.default', $urlParts[0]);
}
Where the config/database.php connections is:
'connections' => [
'subdomain1' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
'subdomain2' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
],

Categories