I have a CakePHP 3.4.6 web application. Due to the fact it's working with legacy (not written in Cake convention) database tables, we have opted to hand-code all of our SQL queries rather than trying to set up Table and Entity classes. I realise some people will disagree with this, but that's not the point of the question.
I have 3 databases: app_db, users_db, extra_db.
I've configured connections to each one in config/app.php such that:
'Datasources' => [
'default' => [], // 'app_db' credentials
'users_db' => [], // 'users_db' credentials
'extra_db' => [], // 'extra_db' credentials
];
I have a custom Model file located at src/Model/MyModel.php. An example of it being used to do a query on one of the databases (app_db) works like this:
namespace App\Model;
use Cake\Datasource\ConnectionManager;
class MyModel
{
protected $connection;
public function __construct()
{
$this->connection = ConnectionManager::get('default');
}
public function getData()
{
$sql = ''; // some SQL query
$stmt = $this->connection->prepare($sql);
$stmt->execute();
return $stmt->fetchAll('assoc');
}
}
This works because it gets the connection for default which maps to app_db.
But, I now want to run a query which needs to get data from app_db and JOIN with data from users_db.
How do I configure this in the Model so that the SQL will communicate with the other databases?
Simple way would be, to have another method, which will change connection.
public function switchConnection($connection=''){
$this->connection = ConnectionManager::get(connection);
}
end call it for change connection
MyModel->switchConnection('app_db');
next time you run getData, it will communicate with app_db instead of default.
Related
Working on multi-database, multi-connection project in Laravel 5.8.x version.
I created a Model for fetching data from specific database using specific connection.
I have defined the connection variable in Model file as:
protected $connection = '[ConnName]';
And I have the said connection set in config/database.php as:
'connections' => [
'[ConnName]' => [
'username' => env('username'),
....
],
....
]
Still when I try to load the Model's data into Controller's Index action as:
$modelData = Model::orderBy('created_at', 'DESC');
exit(print_r(compact($modelData)));
return view('entity.index', compact('modelData'));
It prints null/no records, even though there is data in the database linked with model thro' said connection.
Can anyone suggest anyway to cross-check, if the connection credentials are properly parsed thro' env method or just how to print the utilized connection in controller ?
Make sure that the Model has ->get()
$modelData = Model::orderBy('created_at', 'DESC')->get();
dd($modelData);
This question already has answers here:
Laravel: connect to databases dynamically
(8 answers)
Closed 3 years ago.
I have the master database with login table and corresponding database settings for each user. On login I should dynamically change the db settings fetching from the table.
I can change the db connection but this is not persisting.
Config::set("database.connections.mysql", [
'driver' => 'mysql',
"host" => $usr_host,
"database" => $usr_database,
"username" => $usr_username,
"password" => $usr_password,
...
]);
edit:
New database is created for each user when he/she registers with the app and thus i dont have the database connection for each user defined in the config/database.php
This way you can set new parameter when it comes to database:
\Config::set('database.connections.mysql.database', $schemaName);
Remember about PURGE to persist this settings
DB::purge('mysql');
Cheers!
Well you can use the default database for user login and have a new field for the database name. Then whenever you need to query a different database, you can just change your db connection.
Something like this
$someModel = new SomeModel;
$databaseName = "mysql2"; // Dynamically get this value from db
$someModel->setConnection($databaseName);
$something = $someModel->find(1);
You can read more about it here.
http://fideloper.com/laravel-multiple-database-connections
you need to get the config first.. then alter the specific field then set it back..
$config = Config::get('database.connections.company');
$config['database'] = "company_tenant_$id";
$config['password'] = "test2123";
config()->set('database.connections.company', $config);
I think a good place to change the database connection place is in bootstrap/app.php file, use the code below:
$app->afterBootstrapping(\Illuminate\Foundation\Bootstrap\LoadConfiguration::class, function ($ap) {
// your database connection change may happens here
});
It is before ServiceProvider register and boot, so even if you use DB or Eloquent staff in ServiceProvider, it works very well.
In laravel what you can do is create the different connections in the connections array of the file conf/database, then these connections you can use them when you are going to carry out operations in your application.
If you use query builder or raw expresions you must use the connection ('name') method to perform queries, for example:
$users = DB::connection('mysql')
->table('users')
->select(...)
->get();
If you use eloquent you can specify the name of the connection in the model file in the connection attribute, for example:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The connection name for the model.
*
* #var string
*/
protected $connection = 'mysql';
}
A solution to your problem could be that you have created the different types of connections according to the users in the file conf/database, and save the name of the connection that the user uses as a column in the user table, and when you go to make the queries you get the name of the connection of the user, for example:
$user = User::find(Auth::id());
$connection = $user->connection;
$users = DB::connection($connection)
->table('users')
->select(...)
->get ();
more info:
https://laravel.com/docs/5.5/eloquent#eloquent-model-conventions
https://laravel.com/docs/5.5/database#read-and-write-connections
After an extensive search I found it this way:
Go to this file vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php
Go to the method: protected function authenticate($request, array $guards)
and right after the method has started, paste the following code:
if(auth()->check() && !empty( auth()->user()->db_name )){
$dynamic_db_name = auth()->user()->db_name;
$config = \Config::get('database.connections.mysql');
$config['database'] = $dynamic_db_name;
$config['password'] = "Your DB Password";
config()->set('database.connections.mysql', $config);
\DB::purge('mysql');
}
first we are checking if the user is logged in with
auth()->check()
Then as you may have added db_name name or the like column table to your users table according to each user. So in the next condition, I am making sure that the db_name is available:
&& !empty( auth()->user()->db_name )
Then after execution enters the if condition I get the db_name from the user record and set the configuration according to the user database, save the config and use purge method of DB class to persist this setting.
I was really stuck with it. I did not wanted to change my db connection in every class. So this is how I got it. Now I can use both Eloquent and DB without any tension anywhere. In my application I have one centeral database for login of all users and then for each organization there is a different database. So after the user has logged in, I do not need the centeral database (login database), I juse need the user/organization specific database. So this is how I got it to work.
I'm using Tymon's JWT in lumen with this version > "tymon/jwt-auth": "^1.0#dev" on my composer.
How can I pass parameters on the attempt but will not actually use it for on the sql query?
Currently, I am able to filter by username and password and will return the token:
LoginController.php
$token = $jwt->attempt([
'username' => $request->get('username'),
'password' => md5($request->get('password'))
]);
But since I am using multiple database per site, I want the application to determine what site_code it is using and set the database base on the site_code given.
Example:
If I am using site A, it should use database_a, if site B then database_b and so forth. Currently, I can manually set what database connection and table to use on the User model.
Example:
User.php
public function __construct()
{
$this->setConnection('database_a');
$this->setTable('users');
}
But since the application handles different sites, it passes a site_code on post (aside from the username and password) and determines which database to use base on the site_code.
How could I set the database connection and table base on the site code given?
What I tried is to pass the site code on the attempt like this:
$token = $jwt->attempt([
'username' => $request->get('username'),
'password' => md5($request->get('password')),
'siteCode => $request->get('siteCode')
]);
So I could do like so on the User model:
public function __construct()
{
switch($this->siteCode) {
case 'A':
$this->setConnection('database_a');
break;
default:
$this->setConnection('database_b');
}
$this->setTable('users');
}
But what is happening is that it actually uses the parameters passed on the attempt method for sql. So it gives me an error like this:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'siteCode' in 'where clause' (SQL: select * fromuserswhereusername= test and password = test andsiteCode= A limit 1)
Is there a way to get the siteCode request and pass it on to the User model to determine the database connection but not use it for the sql query?
I have found a way by doing like so. But would like to know if this is the right way to do it or are there any other "proper way" to do it? Having one API to handle multiple sites and databases is applicable if the database structure of those sites are the same. An example of this are blog sites that have the same database structure across the board that shares the same services.
User.php (model)
class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
{
//some codes
public function __construct()
{
$siteCode = app('Illuminate\Http\Request')->get('siteCode');
switch ($siteCode) {
case 'A':
$this->setConnection('database_a');
break;
default:
$this->setConnection('database_b');
break;
}
$this->setTable('users');
}
//some codes
}
I have already database connected in config folder database.php file.
Is there any variable that can simply refers to that MySQLi connection, so that i can use the connection and do manual insertion like-
mysqli_query($alreadyconn, "insert into table ...");
insert() in Codeigniter.com
I know how to insert in codeigniter but i need the above specific case. and here i can not reconnect that database and use the variable.
Any Help Much Appreciated !
Use $this->db to refer to your db connection
$query = $this->db->query("
YOUR QUERY HERE
");
Ok. Seems you know about database.php. Will get know some more
$db['default'] = array(
'dsn' => '',
'hostname' => 'localhost', # host
'username' => '', # Username
'password' => '', # password
'database' => '', # Databse name
'dbdriver' => 'mysqli', # this can be MySQL or MySQLi
Note: Recommend to use MySQLi as db driver.
How to connect database??
There are Three ways to archive this
Gobally - (path - application/config/autoload.php)
$autoload['libraries'] = array('database');
Within the class only.
class ClassName extends CI_Model
{
public function __construct()
{
parent::__construct();
$this->load->database();
}
}
Can use in all the methods added within this class
Withing Methods only. (Mostly on controllers only)
class ClassName extends CI_Controller {
public function __construct()
{
parent::__construct();
}
public function index()
{
$this->load->database();
}
Can use inside the class only
What is Query Builder (3.0+) or Active Records(3.0-) ??
This pattern allows information to be retrieved, inserted, and updated in your database with minimal scripting. In some cases only one or two lines of code are necessary to perform a database action. CodeIgniter does not require that each database table be its own class file. It instead provides a more simplified interface.
How to query with the database & (Query Builder or Active Records) ??
First Load DB in autoload.php mention above(personally recommended).
Where ever you are in model or controller just use $this->db to get open connection with your DB.
With that you can add Query Builder methods like mention in the codeigniter docs. if you are like to use traditional SQL method, you feel free to use query() (Which is my favorite).
Example
$this->db->query("SELECT * FROM user WHERE name = 'Sparatn' AND ... OR... ");
But this not enough to produce meaningful data. So you have to follow these steps as well
$query = $this->db->query("SELECT * FROM user WHERE name = 'Sparatn' "); # 1
$result = $query->result_array(); # 2
return $result; #3
In #1 assign the database row data to an variable.
In #2 Convert the #1 to objective array(result_array objective array represent by this).
In #3 return the result to controller.
To Insert Data In Database you must do not need MySQL function in code igniter .
You should have to use
$this->db->insert()
function to store data in database .
For More Help kindly visit
https://www.formget.com/insert-data-into-database-using-codeigniter/
Thank You
None really answered to the question. The question is clear: how to recycle the codeigniter database connection to be used with a native mysqli_query function without using mysqli_connect again.
The upvoted answers above aren't helpful by any means to the issuer's question.
Use $this->db->conn_id as database connection reference in your $alreadyconn variable.
I am trying to connect with Heroku connect table via CakePHP 3. For some reason, I get the following error when I try to connect with a table whom name ends in '__c'
PHP Fatal error: Call to a member function newEntity() on boolean
Previously, I solved fundamental connection problem that I had in CakePHP at
Heroku Connect with Cakephp v3.0.12 form.
So I could connect with one that doesn't have '__c' in its table name. From the error msg, I understand for some reason my cake app failed to connect with the table I want to connect.
In my App/Model/Table/someFieldTable.php, I have
public function initialize(array $config)
{
parent::initialize($config);
$this->table('salesforce.some_field__c');
$this->displayField('name');
$this->primaryKey('id');
}
I also have the following in my tableController.php
$somefield = $this->someField->newEntity();
// variables are assigned to $somefield
if($this->someField->save($someField)){
// error handling here
}
I am still new to CakePHP and Heroku connect. If anybody knows how to connect with these field (table) with postfix '__c' in CakePHP, Please help me.
Using the TableRegistry class is an effective answer, here is the correct method of getting the autowired table in the controller working:
As you've already been informed, your file naming scheme is incorrect, but that's not the full solution to your table name formatting with Heroku. Your entry within $this->table() should not be namespace to a database with a dot, as the database is appending via the current connection (which is most likely the default datasource defined in app.php) you are making queries on. The entire fix would consist of:
// 1. Change the file name to the correct scheme: SomeFieldTable.php
// 2. In order for the controller to autowire the table, you must correctly
// name the controller file as well: SomeFieldController.php
// 3. Change the table name within SomeFieldTable.php to the appropriate
// name: 'some_field_c'
public function initialize(array $config)
{
parent::initialize($config);
$this->table('some_field__c');
$this->displayField('name');
$this->primaryKey('id');
}
// 4. Finally, in the controller, the table is accessed via the camel capsed name
class SomeFieldController extends AppController
{
public function getEndpoint()
{
$result_set = $this->SomeField->find()->all();
$this->set('result_set', $result_set);
}
public function saveEndpoint()
{
$new_some_field = $this->SomeField->newEntity($this->request->data);
if ($this->SomeField->save($new_some_field)) {
$this->set('new_some_field', $new_some_field);
} else {
$this->set('errors', $new_some_field->errors());
}
}
}
Thanks to ndm Cake is sensitive to its class name and letter cases when I use these special classes.
I also solved the problem over the night.
In my controller, I added individual entity classes additionally to table class.
use App\Model\Entity\SomeFields;
use Cake\ORM\TableRegistry;
and When I create data object, I use manually construct these classes instead to use newEntity()
$someFieldTable = TableRegistry::get('BottomSheet');
$someField = new SomeFileds();
Now I can assign variable to data object manually
For instance
$someField->fieldName = data['fieldName'];
In order to save the data, I now have to manually call the save function
$someFieldTable->save($someField)
and ... Voila!
This is my dirty type of solution, I should fix the name of classes and files properly tho.
Thank you again for the help ndm!