I have two databases for my Laravel 8 project: DB_COMMON and DB_SYSTEM. The point is, that I want to separate sensitive data to avoid deleting, so I'll use one db_user (with all privileges) for DB_COMMON and another (without deleting or updating permissions) for DB_SYSTEM.It will be something like additional security layer.
And there are table/(-s), for exapmle, rbac_role. I want to store two roles as "system" (S_ADMIN and CUSTOMER) in DB_SYSTEM and allow user to create new roles in same table name (rbac_role), but in DB_COMMON. And usually I want to work with that roles in one place (one model).
Are there any way to do it?
Or, maybe, I can "push" this two roles in model? I mean merge rows from database table with my (maybe even hardcoded) rows in model?
First of all you have to create two separate connection in your config/database.php file.
mysql connection is default if you are using mysql database.
Copy that params and create new connection:
'connection2' => [
'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', ''),
...
],
Then you can specify any model to run on specific connection:
class RoleModel extends Model {
protected $connection = 'connection2';
...
}
When you call RoleModel it will connect to second database.
I don't fully get what you want, But if you want to select from rows from two database you can set connection dynamically and select rows from any table.
$first_rows = RoleModel::setConnection('mysql')->select('id', 'role')->get();
$second_rows = RoleModel::setConnection('connection2')->select('id', 'role')->get();
$combined_rows = $first_rows->merge($second_rows);
If you want to create new row in second connection depending whether same record exists in first connection:
if(RoleModel::setConnection('mysql')->where('role', 'somerole')->exists()){
RoleModel::setConnection('connection2')->create([
'role' => 'somerole'
]);
}
Hope this answer helps you
Related
I have a very specific situation regarding databases in Laravel.
We have two DIFFERENT servers, one with SQL SERVER and one MySQL.
The regular join (belongsTo, hasMany) between two different connections and different servers is working flawlessly and I can get all the data that I want.
The problem occurs when I want to add WHERE parameters to the relationship - Laravel will add subquery "and exists" into the query - which will, of course, fail because we have two different servers.
Both models have correction table and connection specified in model properties and as I said regular belongsTo and hasMany is returning the correct results from both servers. Only WHERE conditions are failing the query.
What are the ways to solve this problem and how do you usually deal with this?
Much appreciated!
I'm unsure if that's solving your problem but you could try the package laravel-cross-database-subqueries by hoyvoy.
Just extend your models with Hoyvoy\CrossDatabase\Eloquent\Model.
I had a similar requirement a while ago. The solution I settled on was to replicate the data via a cron job from the MSSQL server into my MYSQL server, but this was largely because the MSSQL server was extremely slow in comparison to the MYSQL server. This allowed me to query correctly.
Alternatively, if you don't want to replicate the data and can get good response times from the MSSQL server, then you may want to look at "temporary tables". If you query the MSSQL server first and then create a temporary table in the MYSQL server with this data to query against, this may suit as a work around.
If none of the above suit, you will need to query the two separately and use PHP to manipulate the data because you can't query MYSQL and MSSQL in the same query.
I see 2 options, the first one is duplicate the database, but you might run behind so perhapse not the best method.
The second option create a seperate query for the relationship filter and use the result of that query to fetch the data
First create connection on config/database.php
return [
'default' => 'first_db_connections',
'connections' => [
'first_db_connections' => [
'driver' => 'sqlsrv',
'host' => 'localhost',
'database' => 'database1',
'username' => 'user1',
'password' => 'pass1'
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
],
'second_db_connection' => [
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'database2',
'username' => 'user2',
'password' => 'pass2'
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
],
],
],
So your models use the normal default connection, then create model second connection to your models
class myModel extends Model {
protected $connection= 'second_db_connection';
protected $table = 'myTable';
public function post() {
return $this->belongsTo('App\Post');
}
}
I have a very, VERY weird issue. Our team has ignored this for a while because it hasn't broken anything (I know, not a good attitude), but I really want to know what the hell is/could be going on. We are on Laravel 5.2.
We have built very long queries using the laravel eloquent query builder. For some reason, sometimes, when we do a toSql(), we'll get the query we meant to run. When pasting the query, I'll get an error like this one:
Error Code: 1055. Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'onion.distance' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
Weird part though, changing nothing about it, laravel will run the query just fine! Anyone have ANY idea why laravel would run the query fine, but the toSql out put trigger this error? And how could we avoid it (other than manually adding the Group By's).
Not pasting code unless requested (lots of moving parts to some of the queries).
Thank you to Marcin Nabialek, you were 100% right. Laravel was overriding the default mysql server configuration and setting strict mode to false. For anyone that was confused as I was:
database.php
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '3306'),
'dump_command_timeout' => 60 * 15, // 5 minute timeout
'unix_socket' => env('UNIX_SOCKET', ''),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', 'mysqltest'),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
]
All I had to do was set strict to true
'strict' => true
I'm making a multi tenancy application. For reasons I've chosen to go with a database for each of the tenants, and then a "master" database which contains meta information about the different tenants, and such.
I've therefore grouped my migrations into two directories:
Master - which contains the migrations for the master database.
Tenants - contains the migrations for each tenant database.
Instead of having to specify the path to the migration folders and the database to run on, each time I migrate, I've created a console command instead. However this is where the issue occurs.
I handle tenants using the subdomain as an identifier for which database to load from, using a middleware like this:
public function handle($request, Closure $next)
{
$domains = explode('.', parse_url($request->url())['host']);
if (count($domains) < 3)
return app()->abort(403, "A valid subdomain is required");
\Config::set('database.connections.tenant.database', $domains[0]);
return $next($request);
}
This works fine for web.
However when I use Config::set() within my console command, it's being ignored, and Laravel is just using the one from my .env file.
database config file:
return [
'default' => env('DB_CONNECTION', 'tenant'),
'connections' => [
'tenant' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_TENANT_DATABASE'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', '')
],
'master' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_MASTER_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
],
]
];
(I've cleaned it up a bit, so only the essential is shown).
In my .env file I've then specified a default tenant database, which is used when I'm using the cli.
DB_HOST=127.0.0.1
DB_MASTER_DATABASE=master
DB_TENANT_DATABASE=company
DB_USERNAME=root
DB_PASSWORD=
Within my TenantMigrater I iterate through each tenant, and run migrate for each of them with a different connection and path.
foreach(Tenant::all() as $tenant)
{
$this->info("Running migrations for the tenant: {$tenant->name}");
\Config::set('database.connections.tenant.database', $tenant->database);
$this->call('migrate', [
'--database' => 'tenant',
'--path' => 'database/migrations/tenants/'
]);
}
Although I'm setting a new database for the tenant connection, it's still falling back to the one specified in the .env file.
I tried going through Laravel's migrater, and as far I could see, the name was being set in the config correctly, so I'm feeling a bit confused. Any ideas?
Edit:
I think I've gotten one step closer to the issue. It seems like, when running php artisan *, the cli starts a connection to the database under the name specified in the config/database.php file. When i then try to override this, the connection is already open to the connection (or so laravel thinks), and it just hand my command the same connection, without setting the new database, hence why it keeps using the same database. However, I have no idea as to how to force it to create a new connection each time I iterate through the tenant.
Running \DB::reconnect('tenant'); before each migration seemed to make it work. This is a bit counter intuitive I think though.
In my Laravel 5 config/database.php file I have the following driver defined.
'Portal' => [
'driver' => 'sqlsrv',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_PORTAL', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'prefix' => 'Portal.dbo.',
],
When I add a period . in the prefix (currently set to Portal.dbo.), I get a white screen with no exception or stack trace information. I have enabled DB logging, but the page stops loading before any logging can take place. When I remove the . from the prefix, everything appends correctly to the query as it should.
I need to prefix my queries in such a way because I have more than one databases on the same SQL Server that I want to run Joins between. How can I use periods in my prefix without Laravel crashing?
I'm trying to create a model and a migration for that model in Laravel 4.2. All of my Laravel apps use the same MySQL database called laravel. However, we also have another database (on the same server), called main_db that contains a users table, which I would like to use for the source of a few foreign keys in my own laravel_users table in the laravel database.
According to the Laravel documentation, I would designate a foreign key with this code:
$table->foreign('user_id')->references('id')->on('users');
But I believe this assumes that the 'users' table exists within the same database.
Is it possible to do trans-database foreign keys in Laravel? Would I have to first create a model that uses the users table from main_db? And is it possible to set up two different database connections in the app/config/database.php?
Cross database foreign keys hasn't much to do with Laravel actually. The real question is if the database does support it. And MySQL (at least with InnoDB) does support foreign key constraints accross multiple databases. You just have to specify the database with the dot notation: db.table.
Regarding the Laravel schema builder, this should work:
$table->foreign('user_id')->references('id')->on('main_db.users');
// ^^^^^^^
If you get an error, check if the column types are the same.
(You can't reference varchar to int or vice versa, keys have to be of same type).
For future reference if you ever change the name of the database this will not work. It is best to grab the name of the database by the database definition.
config/database.php
'auth_database' => [
'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,
],
Then in your migration file:
Schema::create('role_user', function (Blueprint $table) {
$db = DB::connection('auth_database')->getDatabaseName();
$table->integer('role_id')->unsigned();
$table->foreign('role_id')->references('id')->on('roles');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on(new Expression($db . '.users'));
});
Answer of #lukasgeiter worked for me with following two changes:
1- I changed both database engines to InnoDB.
2- I had to set the foreign key to be an Unsigned integer in the migration file as the reference was an unsigned integer and was the primary key.
$table->integer('user_id')->unsigned()->change();