What I want, is to access a separate database, based on the subdomain.
All other settings keep the same: username, password etc. Only database name needs to change.
I add This to database.php file
'subdomain' => [
'driver' => 'mysql',
'host' => '',
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],
I also created Middleware
`$subdomain = $request->route()->account;
$customer = Customer::where( 'sub_domain', $subdomain )->first();
if( ! $customer ) {
// account not found, do something else
} else {
Config::set('database.connections.subdomain.host', 'testdomain');
}
return $next($request);`
this is in route.php
Route::group(['domain' => $domain, 'middleware' => 'subdomain'],function () {});
Use protected $connection = 'subdomain'; in Model
in .env I use default database for check customer database name
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ispsoft
DB_USERNAME=root
DB_PASSWORD=
I got subdomain user database name. But not connected sudomain database And I did not get value from subdomain database
Try setting your model's $table, based on the subdomain. If it's merely a different mysql schema, you should be able to do something like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Widget extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
$subdomain = Config::get('database.connections.subdomain.host', 'testdomain');
protected $table = $subdomian.'.widgets';
}
TestModel.php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;;
class TestModel extends Model
{
protected $table = 'test';
public static function index() {
$configs = DB::select('select * from tests');
}
}
This is my code. I have a file in /public folder name Test.php and want to use DB. But when i call the index() function in Test.php, it returns error.
PHP Fatal error: Uncaught RuntimeException: A facade root has not been set
Test.php saved in public folder
use App\Http\Models\TestModel;
$configs = TestModel::index();
Please help. Thank you!
When you are using anything in public folder without calling index.php it will not initiate the database so to overcome this issue :
/**
* Using For Database Configuration
*/
define('DB_TYPE', 'mysql');
define('DB_USER', 'username');
define('DB_HOST', 'localhost');
define('DB_PORT', '3306');
define('DB_PASSWORD', '');
define('DB_NAME', 'db-name');
$test = new TestModel;
$test->addConnection([
'driver' => DB_TYPE,
'host' => DB_HOST,
'port' => DB_PORT,
'database' => DB_NAME,
'username' => DB_USER,
'password' => DB_PASSWORD,
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
]);
$test->setAsGlobal();
$test->bootEloquent();
$configs = $test->index();
Also, you have to replace one line in TestModel.php
replace use Illuminate\Support\Facades\DB;; with use Illuminate\Database\Capsule\Manager as DB;
Now when you run this command this will be working for you.
I set up Laravel Passport and currently I am trying to register user with a Post Route. I did create a RegisterController inside Controllers/Api/Auth.
Thus I created a clients table which looks excatly like a users table.
The client gets created if I call the route, but I do not get an access token nor a refresh token.
The route to my controller looks like this (routes/api):
Route::post('register', ['as' => 'register', 'uses' => 'Api\Auth\RegisterController#register']);
My Controller looks like this:
<?php
namespace App\Http\Controllers\Api\Auth;
use App\Client;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Route;
use Laravel\Passport\Client as PClient;
use Illuminate\Http\Request;
class RegisterController extends Controller
{
private $client;
public function __construct() {
$this->client = PClient::find(1);
}
public function register(Request $request)
{
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:6|confirmed'
]);
$client_user = Client::create([
'name' => request('name'),
'email' => request('email'),
'password' => bcrypt(request('password'))
]);
$params = [
'grant_type' => 'password',
'client_id' => $this->client->id,
'client_secret' => $this->client->secret,
'username' => request('email'),
'password' => request('password'),
'scope' => '*'
];
$request->request->add($params);
$proxy = Request::create('oauth/token', 'POST');
return Route::dispatch($proxy);
}
}
This is my Client Model:
class Client extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword, HasApiTokens, Notifiable;
protected $table = 'clients';
protected $fillable = ['name', 'email', 'password'];
protected $hidden = ['password', 'remember_token'];
When I am trying to call it with Postman I get this error message:
I may be way off basis here but it looks as if you are creating your client with a password of "password" due to your bcrypt('password') call.
Should it not be bcrypt(request('password'))?
This would explain why your credentials are wrong in your request, because they are ; )
Ok I fixed it, the post route worked if I used the User Model instead of my Client model, so I guessed that there has to be something different.
After some research I have found out that one needs to add the model, in my case the client model to the providers array inside config/auth.php.
So first one needs to change the api guard like this:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'clients',
],
],
This way to api routes login and register only take action with my clients.
Now you need to a a new provider in this case a clients provider like this.
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'clients' => [
'driver' => 'eloquent',
'model' => App\Client::class
],
],
And voila I get an access token + refresh token if I call the route.
I build an application for many tenants and each tenant has it's own database. The name of the database is the same as the tenants id.(the id exists out of five numbers). After authentication the user should be connected with his own database and redirected to his dashboard.
I tried to connect the user with his database with the following code, which I placed in Authenticate.php
if (!Auth::guest() && Auth::user()->tenant) {
$user = Auth::id();
Tenanti::driver('user')->asDefaultDatabase($user, 'users_{id}');
Config::set('database.connections.mysql.database', 'user_'.$user);
}
The if statement checks in the main database if the logged in user is a tenant(boolean).
The config/database.php contains the following code:
'tenants' => [
'user_1' => [
'driver' => 'mysql',
'host' => 'localhost', // for user with id=1
'database' => '86097', // for user with id=1
'username' => 'root', // for user with id=1
'password' => 'root', // for user with id=1
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
],
AppServiceProvider:
<?php namespace App\Providers;
use Orchestra\Support\Facades\Tenanti;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Tenanti::setupMultiDatabase('tenants', function (User $entity, array $template) {
$template['database'] = "user_{$entity->getKey()}";
return $template;
});
}
}
?>
I don't get an error, but the connection hasn't changed. The user database is empty and therefore I shouldn't see any data when I log in with user_id=1 . Thanks in advance for helping.
The configuration should be:
'tenants' => [
'driver' => 'mysql',
'host' => 'localhost', // for user with id=1
'database' => '86097', // for user with id=1
'username' => 'root', // for user with id=1
'password' => 'root', // for user with id=1
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
Other than that please replace Tenanti::setupMultiDatabase() with Tenanti::connection() (we have deprecated the old method).
And change the following:
Tenanti::driver('user')->asDefaultConnection(Auth::user(), 'tenants_{id}');
Obviously if you want to use users_{id} you would then need to replace all tenants to users.
I've seen in several places to "stay away" from this, but alas - this is how my DB is built:
class Album extends Eloquent {
// default connection
public function genre() {
return $this->belongsTo('genre');
}
and the Genre table:
class Genre extends Eloquent {
protected $connection = 'Resources';
}
My database.php:
'Resources' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'resources',
'username' => 'user',
'password' => 'password',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'my_data',
'username' => 'user',
'password' => 'password',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
and when I try to run
Album::whereHas('genre', function ($q) {
$q->where('genre', 'German HopScotch');
});
it doesn't select properly (doesn't add the database name to the table "genres"):
Next exception 'Illuminate\Database\QueryException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'my_data.genres' doesn't exist
Its important to note that this works perfectly:
Album::first()->genre;
Update
The best I've found so far is to use the builder's "from" method to specifically name the correct connection.
I've discovered that the builder inside the query can receive "from"
Album::whereHas('genre', function ($q) {
$q->from('resources.genres')->where('genre', 'German HopScotch');
});
This is a decent solution but it requires me to dig in the database php and find a good way to get the proper table and database name from the relation 'genre'.
I will appreciate if anyone else can build on this solution and make it more general.
Solution for laravel v5.7 and above
class Album extends Eloquent {
// default connection
public function genre() {
return $this->setConnection('Resources')->belongsTo('genre');
}
...
}
This is the way it worked for me:
In my .env and config/database.php i have defined my other connection => How to use multiple databases in Laravel
I updated my model this way:
class MyOtherDBModel extends Model
{
protected $table = 'tablename';
protected $connection = 'mysql2';
public function __construct(array $attributes = [])
{
$this->table = env('DB_DATABASE_2').'.'.$this->table;
parent::__construct($attributes);
}
}
class MyModel extends Model
{
public function myOtherModel()
{
return $this->belongsTo(MyOtherDBModel::class, 'field', 'field');
}
}
Now i can call
$query = MyModel::whereHas('myOtherModel');
This is my own solution and it works in general for me but its mega-complicated.
I'm using the builder "from" method to set the table and database correctly inside the subquery. I just need to pass the correct information inside.
Assume the subquery can be as complicated as "genres.sample" or even deeper (which means albums has a relation to genres, and genres has a relation to samples)
this is how
$subQuery = 'genres.samples';
$goDeep = (with (new Album));
$tableBreakdown = preg_split('/\./', $subQuery); // = ['genres', 'samples']
// I recurse to find the innermost table $album->genres()->getRelated()->sample()->getRelated()
foreach ($tableBreakdown as $table)
$goDeep = $goDeep->$table()->getRelated();
// now I have the innermost, get table name and database name
$alternativeConnection = Config::get("database.connections." . $goDeep->getConnectionName() . ".database"); // should be equal to the correct database name
$tableName = $goDeep->getTable(); // I have to use the table name in the "from" method below
Album::whereHas($subQuery, function ($q) use ($alternativeConnection, $tableName) {
$q->from("$alternativeConnection.$tableName");
$q->where(....... yadda yadda);
});
tl:dr;
Album::whereHas('genres', function ($q) {
$q->from('resources.genres')->where(....);
});
It looks like Eager Loading will do what you want to do
Album::with(['genre' => function ($q) {
$q->connection('Resources')
->where('genre', 'German HopScotch');
}]);
you should clone before otherwise you're changing the default connection of the model. it creates side effect.
class Album extends Eloquent {
public function genre() {
$newResource = clone $this;
return $newResource->setConnection('Resources')->belongsTo('genre');
}
}
Add the connection variable with the default connection on the genre model:
protected $connection = 'mysql';
I had some problems with the relationships by not adding this.
I was facing the same issue on Laravel 5.6. On a Many-to-Many scenario, and supposing the connection from my ModelA was the default one, what I did was the following:
1.- Prefix the schema name in the relationships:
// From ModelA and default connection (a.k.a connection1)
$this->belongsToMany('ModelB', 'schema.pivot-table-name');
// From ModelB and connection2
$this->belongsToMany('ModelA', 'schema.pivot-table-name');
2.- Overwrite connection parameter within the ModelB class and also specify the schema as a prefix in the overwritten $table attribute e.g.
protected $connection = 'connection2';
protected $table = 'connection2-schema-name.table';
3.- In case of requiring a custom behavior for the pivot table, what I did was just to implement the required model and specify it via the ->using('PivotModel'); function on the models relationships (as stated in the documentation). Finally I did the same as in the point 2 above, but on the pivot model
I haven't tried it yet, but I guess the same can be done for other kind of relationships, at least for the basic ones (One-to-One, One-to-Many, etc)
Just in case someone reaches here.
When you are fetching data using a relationship from different database connections, make sure that your related table has the $connection property defined even if it's the default connection.
Account Model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Account extends Model {
protected $connection = 'different_connection';
public function verifiedBy()
{
return $this->belongsTo(User::class, 'verified_by');
}
}
User Model:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable {
// this one uses the default - `database.default` connection
public function approved()
{
return $this->hasMany(Account::class, 'verified_by');
}
}
As soon as I do Account:find($id)->verifiedBy, it will throw error. Look at the database name and you'll find. But if you do, User::find($id)->approved, it will work fine, because the Account model has the connection defined. And not the vice-versa.
So, just to be safe, if you deal with multiple database connections, put the $connection property in the models.
Laravel's implementation
I had the same issue when relationship wasn't working off the model connection.
My solution was to override the belongsToMany method on the model trying to establish. See example below.
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class ConnectionModel extends Model
{
/**
* Override method to allow inheriting connection of parent
*
* Define a many-to-many relationship.
*
* #param string $related
* #param string $table
* #param string $foreignKey
* #param string $otherKey
* #param string $relation
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany|BelongsToMany
*/
public function belongsToMany($related, $table = null, $foreignKey = null, $otherKey = null, $relation = null)
{
// If no relationship name was passed, we will pull backtraces to get the
// name of the calling function. We will use that function name as the
// title of this relation since that is a great convention to apply.
if (is_null($relation)) {
$relation = $this->getBelongsToManyCaller();
}
// First, we'll need to determine the foreign key and "other key" for the
// relationship. Once we have determined the keys we'll make the query
// instances as well as the relationship instances we need for this.
$foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
// get connection from parent
$instance->setConnection(parent::getConnectionName());
$otherKey = $otherKey ?: $instance->getForeignKey();
// If no table name was provided, we can guess it by concatenating the two
// models using underscores in alphabetical order. The two model names
// are transformed to snake case from their default CamelCase also.
if (is_null($table)) {
$table = $this->joiningTable($related);
}
// Now we're ready to create a new query builder for the related model and
// the relationship instances for the relation. The relations will set
// appropriate query constraint and entirely manages the hydrations.
$query = $instance->newQuery();
return new BelongsToMany($query, $this, $table, $foreignKey, $otherKey, $relation);
}
}
1: This is my .env with two database connection
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=foreign_key_test
DB_USERNAME=root
DB_PASSWORD=
DB_CONNECTION_SECOND=mysql
DB_HOST_SECOND=127.0.0.1
DB_PORT_SECOND=3306
DB_DATABASE_SECOND=foreign_key_test_2
DB_USERNAME_SECOND=root
DB_PASSWORD_SECOND=
2: in my config/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', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'mysql2' => [
'driver' => env('DB_CONNECTION_SECOND'),
'host' => env('DB_HOST_SECOND', '127.0.0.1'),
'port' => env('DB_PORT_SECOND', '3306'),
'database' => env('DB_DATABASE_SECOND', 'forge'),
'username' => env('DB_USERNAME_SECOND', 'forge'),
'password' => env('DB_PASSWORD_SECOND', ''),
'unix_socket' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
],
3: State model from other database
class State extends Model
{
use HasFactory;
protected $table = 'foreign_key_test_2.states';
protected $connection = 'mysql2';
protected $primaryKey = 'id';
protected $fillable = [
'name'
];
}
4: Country model from 1st database where i made my relation with other database table
class Country extends Model
{
use HasFactory;
protected $connection = 'mysql';
protected $table = 'countries';
protected $primaryKey = 'id';
protected $fillable = [
'name',
'state_id'
];
public function state()
{
return $this->belongsTo(State::class);
}
}
5: Final step is my route
Route::get('city', function(){
return $country = Country::with('state')->get();
});
6: this is working fine for me. Let me know with your experience.
I found a really good article for this here: http://fideloper.com/laravel-multiple-database-connections
You basically have to specify your two connections in your config file like so:
<?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' => '',
),
),
);
So your two connections are aliased to mysql and mysql2.
Then you can tell eloquent which 'alias' to use like so:
<?php
class SomeModel extends Eloquent {
protected $connection = 'mysql2';
}
Then you can setup your relationships like normal.
tl;dr: Basically instead of specifying the table name as $connection in eloquent, specify the connection alias in your configuration and it should work.
I used the following method to use the default connection.
use Illuminate\Database\Eloquent\Model as MainModel;
use Illuminate\Support\Facades\Config;
class BaseModel extends MainModel
{
function __construct(array $attributes = [])
{
// laravel bug
// in belongsTo relationship, default connection not used
$this->connection = Config::get('database.default');
parent::__construct($attributes);
}
}
To start change 'Resources' in database.php by 'resources', will be better !
I'm curious, can you try that ?
Album::whereHas('genre', function ($q) {
$q->setConnection('resources')->where('genre', 'German HopScotch');
});