How to use global prefix for tables in Laravel 5 - laravel

New in Laravel. Probably a silly question. I had setup database like this:
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '3306'),
'database' => 'mydb',
'username' => 'myusername',
'password' => 'mypassword',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => 'admin',
'strict' => false,
'engine' => null,
],
Notice 'prefix' => 'admin'. This is because I want all tables related to the website's control panel be prefixed with admin, e.g: admin_users, admin_log, etc...
But I'm stuck at the very beginning. I'm trying to create migrations via artisan but it's not creating the tables with the prefix.
php artisan make:migration create_users_table --create=users
I'm expecting that to create a table named admin_users. But it's not.
Am I doing this right?

Laravel caches config files, so you may simply need to clear the cache:
php artisan config:clear
In addition to this, a better practice is to use the .env file to define your prefix, like this:
...
DB_PREFIX=admin_
...
and loading that in your config file, like this:
'prefix' => env('DB_PREFIX', 'abc')
This is how it should have been by default.
You're already loading the hostname and port from the .env file, so why not do it for the other values as well?

The migrations will be created without a prefix. After running php artisan migrate you should see tables with prefixes

Prefix does not includes underscore (_) by itself. In order to create admin_users You have to use admin_ prefix.
When generating migrations You will get plain table names, without prefixes.
With prefix = 'admin_'; this: Schema::create('users', function (Blueprint $table) {<...>}); will result in creation of admin_users table, because Laravel adding Your prefix under the hood by default.
TL;DR
Even though Your scheme displays table name without prefix, Laravel will create it with prefix.

You should use Schema::connection($name) method to apply the connection to the schema builder.
For some reason if you just set connection property - migration uses this connection to run queries but wont uses it during schema building.
The following is the migration code example that uses custom connection to create foo table:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateFoo extends Migration
{
protected $connection = 'custom';
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::connection($this->connection)->create('foo', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::connection($this->connection)->dropIfExists('foo');
}
}
This code is tested in real app and works perfectly.
Laravel version: 8.40

Related

Output is empty when perform a seed in nWidart Laravel

I'm using nWidart package to manager Laravel app using modules.
I perform a migrate on Settings module.
php artisan module:make-migration create_locations_table Settings
php artisan module:migrate Settings
It's ok!
When I make a seeder:
php artisan module:make-seed Locations Settings => It worked!
But: php artisan module:seed Settings
<?php
namespace Modules\Settings\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class LocationsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
Model::unguard();
// $this->call("OthersTableSeeder");
$data = [
'id' => '1',
'name' => 'Hà Nội',
'parent_id' => NULL,
'type' => '1',
'district_id' => NULL,
'province_id' => NULL,
'country_id' => '79162',
'created_at' => '2020-03-26 12:01:08',
'updated_at' => '2020-03-26 12:01:08'
];
DB::table('locations')->insert($data);
}
}
The output is empty!
I think it doesn't go to the LocationsTableSeeder file because when I try dd(1) on it, the output is empty too
Can you help me?
Thanks so much!
Since you're using laravel/framework 5.7.*, you might want to install nwidart/laravel-modules ^4.0 as it is the proper version for you laravel version. See compatibilty.
If that still doesn't work, do this for the meantime:
php artisan db:seed --class=Modules\Settings\Database\Seeders\LocationsTableSeeder
Way forward
Since your project is pretty far behind. I doubt that they will provide any support for this anymore. Upgrading your laravel to ^7.x and laravel-modules to ^7.0 may fix this since there had been an issue about this and has been fixed in the latest versions.

How to prompt for database password when running Laravel 5 migrations

Laravel migrations need to be run with a particularly high level of privilege, as they perform DDL operations. I would like to run migrations as a named user without storing the password anywhere. This will require prompting the user for their password when they run the migration from the command-line. I cannot find any way to achieve this via connection configuration.
Is there a way to achieve this via the connection configuration?
If not, is there a way to perform the migrations with a thin layer of custom code over the top to establish the database connection in a custom manner? e.g. write a script/artisan command that does the prompting, connects to the DB, and then delegates the rest to Laravel's existing migration code?
If your requirement is to just ask the user for database credentials in order to run the migrations, then this can very easily be achieved by wrapping the artisan migrate command within another command that asks for appropriate credentials. So the steps below should suffice:
1. Create the new command:
artisan make:console MigratePrivilegedCommand --command="migrate:privileged"
2. Add the necessary code to handle user input and run the migrations in your new command class:
class MigratePrivilegedCommand extends Command
{
protected $signature = 'migrate:privileged';
protected $description = 'Run migrations as a priviledged database user.';
public function __construct()
{
parent::__construct();
}
public function handle()
{
// Set the username and password for the
// database connection from user input
config([
'database.connections.mysql.username' => $this->ask('What is your username?'),
'database.connections.mysql.password' => $this->secret('What is your password?')
]);
// Call the migrate command and you're done
$this->call('migrate');
}
}
3. Register the new command in App\Console\Kernel.php:
protected $commands = [
...
\App\Console\Commands\MigratePrivilegedCommand::class,
];
And now you can run migrations that need privileged database credentials with this:
php artisan migrate:privileged
You can set a custom connection without any password in the config file and pass the password via CLI command to run the migrations.
In database.php config
'connection_without_password' => [
'driver' => 'mysql',
'host' => 'hostname,
'database' => 'database',
'username' => '$username',
'password' => '$password',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
Schema class includes a connection name option.
Schema::connection('connection_without_password')->create('users', function ($table) {
$table->increments('id');
});

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.

Symfony2 like database creation command in laravel console?

I have used symfony2 console to create database. If I want to create a database named "symfony" I usually mentioned that name in parameters.yml file and run the below command in console
php app/console doctrine:database:create
But when came to laravel, I don't find similar command to create database in laravel. Can anyone help me to find out those command to create database directly from Laravel Console.
You can do that but you will have to create your own command.
First, run php artisan command:make CreateDatabase --command=database:create to generate app/commands/CreateDatabase.php
Then open that file and change it to this: (I left out all comments, but you can obviously keep them in there)
class CreateDatabase extends Command {
protected $name = 'database:create';
protected $description = 'Command description.';
public function fire()
{
DB::statement('CREATE DATABASE '.$this->argument('name'));
}
protected function getArguments()
{
return array(
array('name', InputArgument::REQUIRED, 'Database name'),
);
}
}
Now you only have to register the command in app/start/artisan.php:
Artisan::add(new CreateDatabase);
and you're good to go.
That's how you call it:
php artisan database:create your-desired-database-name
Alternative: artisan tinker
You can always use php artisan tinker to run PHP code (with Laravel bootstrapped):
php artisan tinker
> DB::statement('CREATE DATABASE your-desired-database-name');
As far as I know, you can use php artisan migrate to make migrations including creating tables from Laravel Console. However, you need to create and modify migration files first, where you can create or drop tables.
So if you want to create a table directly from Laravel Console using something like php artisan create database table ***, it is not possible.
I think you can not create database through command so go to app/config/database.php and set your database configuration. details here
'mysql' => array(
'read' => array(
'host' => '192.168.1.1',
),
'write' => array(
'host' => '196.168.1.2'
),
'driver' => 'mysql',
'database' => 'database',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
after setting you can create table through command
php artisan migrate:make create_user_table --table=users
it will generate a file to app/database/migrations name create_users_table. . . . . then you create your table following this link
and finally run this command php artisan migrate

Laravel: Run migrations on another database

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;
}

Resources