Laravel: Run migrations on another database - laravel

In my app every user has its own database that created when user registered. Connection and database data (database name, username, password) are saved in a table in default database.
try{
DB::transaction(function() {
$website = new Website();
$website->user_id = Auth::get()->id;
$website->save();
$database_name = 'website_'.$website->id;
DB::statement(DB::raw('CREATE DATABASE ' . $database_name));
$websiteDatabase = new WebsiteDatabase();
$websiteDatabase->website_id = $website->id;
$websiteDatabase->database_name = $database_name;
$websiteDatabase->save();
});
} catch(\Exception $e) {
echo $e->getMessage();
}
Now I want to run some migrations on new user's database after its creation.
Is it possible?
thanks

If you place database config on the database.php file, this can help you:
php artisan migrate --database=**otherDatabase**

In your app/config/database.php you have to:
<?php
return array(
'default' => 'mysql',
'connections' => array(
# Our primary database connection
'mysql' => array(
'driver' => 'mysql',
'host' => 'host1',
'database' => 'database1',
'username' => 'user1',
'password' => 'pass1'
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
# Our secondary database connection
'mysql2' => array(
'driver' => 'mysql',
'host' => 'host2',
'database' => 'database2',
'username' => 'user2',
'password' => 'pass2'
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
),
);
Now that you prepared two database connections in your migration you can do:
Schema::connection('mysql2')->create('some_table', function($table)
{
$table->increments('id');
});
This should work.
More infos on: http://fideloper.com/laravel-multiple-database-connections

If you mean using different database connection, it exists in the docs:
Schema::connection('foo')->create('users', function (Blueprint $table) {
$table->bigIncrements('id');
});

I have the same problem, my solution is to change the database using Config::set first then run Artisan::call("migrate"). so based on your code:
DB::statement(DB::raw('CREATE DATABASE ' . $database_name));
Config::set('database.connections.mysql.database', $database_name);
Artisan::call("migrate --database=mysql");
the config only changed on your session then reset later as your current setting.

This is tedious to remember which migration corresponds to which database.
For Laravel 5.5 I used this approach:
public function up()
{
// this line is important
Illuminate\Support\Facades\DB::setDefaultConnection('anotherDatabaseConnection');
Schema::table('product',
function (Blueprint $table)
{
$table->string('public_id', 85)->nullable()->after('ProductID');
});
// this line is important, Probably you need to set this to 'mysql'
Illuminate\Support\Facades\DB::setDefaultConnection('nameOfYourDefaultDatabaseConnection');
}
All migrations can be run automatically without taking care of specifying database manually when running them.
Please note that migrations table is stored inside your default database.

I actually faced the same problem and the answer of Joe did not work in my case, as I have different database connections (so different host, port, user and pass).
Therefore the migration must do a lot of reconnects all the time:
Migration starts with default database (in my case that is client_1)
Fetches stuff from table migrations and clients
Disconnect default database
Connect to database of client_2, run migration parts, disconnect client_2
Connect to default database again, store migration "log"
And then loop it for each client.
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
$defaultConnection = BackendConfig::getDatabaseConfigArray();
$clients = ClientController::returnDatabasesForArtisan();
foreach ($clients as $client) {
BackendConfig::setDatabaseFromClient($client);
Schema::create('newtable', function (Blueprint $table) {
$table->increments('id')->unsigned();
$table->timestamps();
});
BackendConfig::setDatabaseFromArray($defaultConnection);
}
}
And the class where the magic is stored:
class BackendConfig
{
public static function getDatabaseConfigArray($client_id = 1)
{
$connection = config('database.default');
return [
'id' => $client_id,
'host' => config("database.connections.$connection.host"),
'port' => config("database.connections.$connection.port"),
'username' => config("database.connections.$connection.username"),
'password' => config("database.connections.$connection.password"),
];
}
public static function setDatabaseFromArray($array)
{
self::setDatabase($array['id'], $array['host'], $array['port'], $array['username'], $array['password'], true);
DB::disconnect();
}
public static function setDatabaseFromClient(Client $client)
{
DB::disconnect();
self::setDatabase($client->id, $client->database->host, $client->database->port, $client->database->username, $client->database->password, true);
}
public static function setDatabase($client_id, $host, $port, $username, $password)
{
$connection = config('database.default');
$database_name = $connection . '_' . $client_id;
config([
"database.connections.$connection.database" => $database_name,
"database.connections.$connection.host" => $host,
"database.connections.$connection.port" => $port,
"database.connections.$connection.username" => $username,
"database.connections.$connection.password" => $password,
]);
}
With this solution I can run the exact same migrations on every client, yet the migration is just stored in client_1, my sort of master client.
However, pay attention to the two DB::disconnect();. It will screw up the situation without those as then migrations logs are stored in another client's database or such.
Ah and by the way, ClientController does nothing special:
public static function returnDatabasesForArtisan()
{
return Client::select('*')->with('database')->get();
}

I think I finally figured out this mess... This solution doesn't need a configuration for each tenant's database and has to be run only once.
class MigrationBlah extends Migration {
public function up() {
$dbs = DB::connection('tenants')->table('tenants')->get();
foreach ($dbs as $db) {
Schema::table($db->database . '.bodegausuarios', function($table){
$table->foreign('usuario')->references('usuarioid')->on('authusuarios');
});
}
}
}
Where I have a connection named "tenants" on my database.php, which contains the database name of all of my tenants. I have the default connection set to my tenants database as well. That database is the one responsible for taking care of the migrations table.
With the foreach statement, it goes through the tenant databases and runs the migration on each one.
On your default connection, you should configure a user that has access to all tenant's databases for it to work.

Best solution is you can call this method on AppServiceProvide
it is the best solution for this type of problem. I am using this in my project. In my case, I have two environments Development and Production. so when the project is development mode then it will look on local Server else Live server. So you can set dynamic-DB concept here.
you have to make a function then you have to call this inside of boot() Function on App\Providers\AppServiceProvide.php
public function boot()
{
DBConnection();
}
I created Helper File for this. so my code in helper.php
function DBConnection()
{
if( env('APP_ENV') == 'local' )
{ $databse_name = "test_me";
$host = '127.0.0.1';
$user="root";
$password="";
}
else
{
$databse_name = 'tic_tac';
$host = 'localhost';
$user="";
$password="";
}
$state = true;
try {
Config::set('database.connections.myConnection', array(
'driver' => 'mysql',
'host' => $host,
'database' => $databse_name,
'username' => $user,
'password' => $password,
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
));
/* \DB::setDefaultConnection('myConnection');
$state = \DB::connection()->getPdo();*/
Config::set('database.connections.myConnection.database', $databse_name);
\DB::setDefaultConnection('myConnection');
\DB::reconnect('myConnection');
} catch( \Exception $e) {
$state = false;
}
return $state;
}

Related

How Do I Prevent Duplicate When Storing My API Fetched Data

I am fetching data from an API. I am able to store the data in my database but whenever i refresh, it stores another copy of the same set of data fetched from the API. I simply want to know how to only update the database with the new records whilst ignoring the old once stored already.
Controller
public function dashboard()
{
$client = new Client();
$uri = 'https://api.clickmeeting.com/v1/conferences/active';
$header = ['headers' => ['X-Api-Key' => '123456']];
$res = $client->get($uri, $header);
$conferences = json_decode($res->getBody()->getContents(), true);
collect($conferences)
->each(function ($conference, $key) {
ClickMeeting::create([
'conference_id' => $conference['id'],
'parent_id' => $conference['parent_id'],
'room_type' => $conference['room_type'],
'room_pin' => $conference['room_pin'],
'name' => $conference['name'],
'name_url' => $conference['name_url'],
'access_type' => $conference['access_type'],
'lobby_enabled' => $conference['lobby_enabled'],
'lobby_description' => $conference['lobby_description'],
'registration_enabled' => $conference['registration_enabled'],
'status' => $conference['status'],
'timezone' => $conference['timezone'],
'timezone_offset' => $conference['timezone_offset'],
'paid_enabled' => $conference['paid_enabled'],
'automated_enabled' => $conference['automated_enabled'],
'type' => $conference['type'],
'permanent_room' => $conference['permanent_room'],
'room_url' => $conference['room_url'],
'embed_room_url' => $conference['embed_room_url'],
]);
});
return view('admin.clickmeeting.dashboard');
}
Migration Schema
public function up()
{
Schema::create('clickmeeting', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('conference_id')->unique();
$table->string('parent_id')->nullable();
$table->string('room_type');
$table->string('room_pin');
$table->string('name');
$table->string('name_url');
$table->string('ends_at');
$table->string('access_type');
$table->string('lobby_enabled');
$table->string('lobby_description');
$table->string('registration_enabled');
$table->string('status');
$table->string('timezone');
$table->string('timezone_offset');
$table->string('paid_enabled');
$table->string('automated_enabled');
$table->string('type');
$table->string('permanent_room');
$table->string('room_url');
$table->string('embed_room_url');
});
}
Help is gratefully appreciated. Thank You
Laravel has a firstOrCreate() method, which either finds the model or creates it. Perfect for your example. The first parameter, is the columns that defines the uniqueness and the second parameter is the columns that should be added to the row. These columns are concatenated when saved. It is very uncommon and flaky, to expect such a big request to use all columns for uniqueness.
Let's imagine that these 3 columns would define a non present composite key room_type, parent_id and room_url. This can be adjusted by you to follow the correct columns.
ClickMeeting::firstOrCreate([
'parent_id' => $conference['parent_id'],
'room_type' => $conference['room_type'],
'room_url' => $conference['room_url'],
],
[
'conference_id' => $conference['id'],
'room_pin' => $conference['room_pin'],
'name' => $conference['name'],
'name_url' => $conference['name_url'],
'access_type' => $conference['access_type'],
'lobby_enabled' => $conference['lobby_enabled'],
'lobby_description' => $conference['lobby_description'],
'registration_enabled' => $conference['registration_enabled'],
'status' => $conference['status'],
'timezone' => $conference['timezone'],
'timezone_offset' => $conference['timezone_offset'],
'paid_enabled' => $conference['paid_enabled'],
'automated_enabled' => $conference['automated_enabled'],
'type' => $conference['type'],
'permanent_room' => $conference['permanent_room'],
'embed_room_url' => $conference['embed_room_url'],
],
]);

BlueFeatherGroup eloquent-filemaker connection problem

I'm using https://github.com/BlueFeatherGroup/eloquent-filemaker in a Laravel Project to get data from a FileMaker database.
I use a MySQL as default connection in my project, but I need a second connection to a FileMaker Server.
I created in database.php config file a new conn:
'filemaker' => [
'driver' => 'filemaker',
'host' => 'myfmhost',
'database' => 'FMdatabase',
'username' => 'FMUser',
'password' => 'MFPass',
'prefix' => '',
'version' => 'vLatest',
'protocol' => 'https',
],
I created a model "tabla" as follows:
use BlueFeather\EloquentFileMaker\Database\Eloquent\FMModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class tabla extends FMModel
{
use HasFactory;
protected $connection= 'filemaker';
protected $fillable = [ ];
protected $layout = 'tabla';
}
In my Controller:
$data = tabla::all();
dd($data);
and I get this error:
Argument 1 passed to
BlueFeather\EloquentFileMaker\Database\Query\FMBaseBuilder::__construct()
must be an instance of
BlueFeather\EloquentFileMaker\Services\FileMakerConnection, instance
of Illuminate\Database\MySqlConnection given
But if I do this in my controller (Query Builder aproach):
$data = FM::connection('filemaker')->layout('tabla')->get();
dd($data);
It works ok!
What am I doing wrong? Is it possible to use filemaker conn when using Eloquent if it's not the default project connection ?
Thanks in advance!!

Dusk not working with factories and/or migrations in Homestead

Using a fresh install of Laravel and dusk, then copying the exact test from the docs for logging in the user, I get an error that the users table doesn't exist.
I don't get any error when using a factory to create a user, but when trying to login as that user, i can see the browser window opening (screenshoots), typing in the creds, then seeing the error that user can't be found.
I see that Dusk tests never ran the migrations.
I change my config/database.php and .env.dusk.local
// config/database.php
'connections' => [
'testing' => [
'driver' => 'sqlite',
'database' => database_path('testing.sqlite'),
'prefix' => '',
],
],
`// .env.dusk.local
APP_URL=http://project.dev
APP_ENV=testing
DB_CONNECTION=testing`
and ran composer update but the error persist :( my test code:
public function setUp() {
parent::SetUp();
factory(Rol::class)->create(['nombre' => 'Administrador']);
$this->usuario = factory(Usuario::class)->create(['email' => 'correo#ejemplo.com', 'password' => bcrypt('123456'), 'rol_id' => Rol::where('nombre', 'Administrador')->first()->id]);
}
/**
* #tests
*/
public function loggeo_usuario() {
$this->browse(function (Browser $browser) {
$browser->visit(route('login'))
->assertSee('Correo')
->type('email', $this->usuario->email)
->type('#password', '123456')
->click('#login-button')
->assertSee('Ayuda');
});
}

Elequent / Illuminate Laravel Database LIKE operation not working

I'm using the Illuminate database manager from Laravel, which works pretty good except using the LIKE operation for now.
I have tried those options but got nothing:
function initConnection()
{
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'mysql',
'host' => $this->config['host'],
'database' => $this->config['database-name'],
'username' => $this->config['username'],
'password' => $this->config['password'],
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => ''
]);
$capsule->setAsGlobal();
}
And after initializing I tried:
function searchByName($word)
{
return Capsule::table($table)
->get()
->where('name', 'LIKE', '%'.$word.'%')
->first();
}
echo searchByName('John');
I also tried this option:
Capsule::table('table_name')
->select('SELECT * FROM table_name WHERE table_name.name LIKE "%John%"');
this also failed.
I can't find a documentation for using all the operations in Laravel.
you don't need to define the table in eloquent like this. your model will do that by itself if you follow the name convention or u can set table name in the model by :
class YourModelName extends Model
{
protected $table = 'your_table_name';
}
now query like this :
function searchByName($word) {
return Capsule::where('name', 'LIKE', '%'.$word.'%')->get();
}
this will return multiple value which will match the string.

Transaction doesn't work when instantiating eloquant from a class

I try to use transactions OUTSIDE laravel. it works when I include the db instantiation right in my index file as is:
use Illuminate\Database\Capsule\Manager as Db;
$db = new Db;
$db->addConnection( [
'driver' => Settings::DATABASE_DRIVER,
'host' => Settings::DATABASE_HOST,
'database' => Settings::DATABASE_NAME,
'username' => Settings::DATABASE_USERNAME,
'password' => Settings::DATABASE_PASSWORD,
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => ''
] );
# Make this Capsule instance available globally via static methods... (optional)
$db->setAsGlobal();
I can then elsewhere use code like this:
Db::connection()->beginTransaction();
# Create blog post in the database and return its id
$blogPostRecordId = ( new BlogDbModel() )->create( $_POST );
Db::connection()-> rollBack();
it will correctly work and rollback: no row is created in db.
However, If I get an instance of the db from a class, it won't work:
class DbSql
{
/**
* Use the eloquent query builder and orm. Bypass PDO interface.
* #return Capsule
*/
public function db()
{
$capsule = new Capsule;
$capsule->addConnection( [
'driver' => Settings::DATABASE_DRIVER,
'host' => Settings::DATABASE_HOST,
'database' => Settings::DATABASE_NAME,
'username' => Settings::DATABASE_USERNAME,
'password' => Settings::DATABASE_PASSWORD,
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => ''
] );
# Make this Capsule instance available globally via static methods... (optional)
$capsule->setAsGlobal();
// Setup the Eloquent ORM... (optional; unless you've used setEventDispatcher())
//$capsule->bootEloquent();
return $capsule;
}
}
and then use
( new DbSql() )->db()->getConnection()->beginTransaction();
# Create blog post in the database and return its id
$blogPostRecordId = ( new BlogDbModel() )->create( $_POST );
( new DbSql() )->db()->getConnection()->rollBack();
it simply won't work and the transaction is ignored. Why does getting the db instance from a class instantiation make the process fail ? I would prefer to use an instance as needed.
Solution: the db instance must be exactly the same everywhere the transaction is needed:
$dbInstance = ( new DbSql() )->db();
Then use $dbInstance wherever the transaction is to be followed:
$dbInstance->connection()->beginTransaction();
as well as with any db eloquent operation. So pass the $dbInstance as needed. The good thing is that it can be anywhere in you code: in your models, controllers....
Then finish:
$dbInstance->connection()->commit();
If the system is not able to reach this last line, nothing will be committed to the db.

Resources