2 databases and libraries

#1
[eluser]Maglok[/eluser]
I have an app where I have a database for the app data and a database from a phpbb forum that provides login data.

In my database.php I have the following:

Code:
/* APP */
$active_group = "default";
$active_record = TRUE;

$db['default']['hostname'] = "***";
$db['default']['username'] = "***";
$db['default']['password'] = "***";
$db['default']['database'] = "***";
$db['default']['dbdriver'] = "mysql";
$db['default']['dbprefix'] = "";
$db['default']['pconnect'] = TRUE;
$db['default']['db_debug'] = TRUE;
$db['default']['cache_on'] = FALSE;
$db['default']['cachedir'] = "";
$db['default']['char_set'] = "utf8";
$db['default']['dbcollat'] = "utf8_general_ci";

/* FORUM */
$active_group = "forum";
$active_record = TRUE;

$db['forum']['hostname'] = "***";
$db['forum']['username'] = "***";
$db['forum']['password'] = "***";
$db['forum']['database'] = "***";
$db['forum']['dbdriver'] = "mysql";
$db['forum']['dbprefix'] = "";
$db['forum']['pconnect'] = TRUE;
$db['forum']['db_debug'] = TRUE;
$db['forum']['cache_on'] = FALSE;
$db['forum']['cachedir'] = "";
$db['forum']['char_set'] = "utf8";
$db['forum']['dbcollat'] = "utf8_general_ci";

I also extended the default Controller as follows:

Code:
<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class MY_Controller extends Controller {

    function MY_Controller() {
        parent::Controller();
        $this->db_forum = $this->load->database('forum', TRUE);
        $this->db_app = $this->load->database('default', TRUE);
    }
}

?>

I can now use $this->db_forum->something to approach the forum db and $this->db_app->something to approach the application db. Here is where it goes wrong. I activated the profiler to see how my queries and were doing and got this:

Code:
A PHP Error was encountered

Severity: Notice

Message: Undefined property: Nieuws::$db

Filename: libraries/Profiler.php

Line Number: 143
A PHP Error was encountered

Severity: Notice

Message: Trying to get property of non-object

Filename: libraries/Profiler.php

Line Number: 143

Looked it up and the library has a reference to $this->CI->db->queries. But 'db' is not in use when using multiple databases. How do I circumvent this issue? Can one be a default for example?

#2
[eluser]WanWizard[/eluser]
looks like a typo, it should be '$db->queries'.

#3
[eluser]Maglok[/eluser]
I don't think a core library has a typo really. That would make it a bug in CI. Libraries/Profiler.php is a core library.

#4
[eluser]rogierb[/eluser]
The profiler class assumes you have one db and use the standard connection.

So you can't use it unless you extend it twice, for each connection once. Or rewrite it to pass the connection as a parameter.

#5
[eluser]Maglok[/eluser]
This whole 2 databases support thing isn't really working out for CI is it? Tongue dbforge was also giving me hell, not supporting a 2nd one.

Oddly the profiler seems to work just fine, showing two queries boxes, and both working, it just throws those notices.

#6
[eluser]Unknown[/eluser]
Hi there,

I also have similar problems with multiple database profiler. My application have two database groups, one is 'master' and the other is 'slave'. 'master' is the active group.

I also write a base model class to make it easier to get the Master or Slave database connection and other model classes will have to extend this class.

Code:
class MY_Model extends Model
{
    public function MY_Model()
    {
        parent::Model();
    }
    
    protected function getMasterDb()
    {
        return $this->load->database('master', TRUE);            
    }
    
    protected function getSlaveDb()
    {
        return $this->load->database('slave', TRUE);    
    }
}

In User model, I have the following function:

Code:
class User_model extends MY_Model
{
    public function getUser($uid)
    {  
        $db = $this->getSlaveDb();        
        $sql = 'SELECT * FROM users WHERE uid = ?';
        $data = $db->query($sql, array($uid));    
        if ($data->num_rows() > 0){
            return $data->row();
        }
        return FALSE;
    }
}

Function getUser runs perfectly but the profiler can not capture the query executed. If I use $this->getMasterDb instead of $this->getSlaveDb, the same problem will occur.

When looking through the CodeIgniter Loader class, I realize that if I set the second parameter of the function $this->load->database to TRUE to force the function return the database object, the database object will not be assign to $CI->db. Therefore, the profiler will not be able to display queries executed on the database.

Code:
function database($params = '', $return = FALSE, $active_record = FALSE)
    {
        // Grab the super object
        $CI =& get_instance();
        
        // Do we even need to load the database class?
        if (class_exists('CI_DB') AND $return == FALSE AND $active_record == FALSE AND isset($CI->db) AND is_object($CI->db))
        {
            return FALSE;
        }    
    
        require_once(BASEPATH.'database/DB'.EXT);

        if ($return === TRUE)
        {
            return DB($params, $active_record);
        }
        
        // Initialize the db variable.  Needed to prevent  
        // reference errors with some configurations
        $CI->db = '';
        
        // Load the DB class
        $CI->db =& DB($params, $active_record);    
        
        // Assign the DB object to any existing models
        $this->_ci_assign_to_models();
    }

I also try to extend the CI Loader class to make sure that each database connection is assigned to a CI Super object's property. The profiler is now working as expected. Two queries boxes are display for Master and Slave connections.

Code:
class MY_Loader extends CI_Loader
{
    public function MY_Loader()
    {
        parent::CI_Loader();                
    }
    
    public function database($params = '', $return = FALSE, $active_record = FALSE)
    {
        // Grab the super object
        $CI =& get_instance();
        
        // Do we even need to load the database class?
        if (class_exists('CI_DB') AND $return == FALSE AND $active_record == FALSE AND isset($CI->db) AND is_object($CI->db))
        {
            return FALSE;
        }    
    
        require_once(BASEPATH.'database/DB'.EXT);    
        
        /**
        * these lines are modified a litle bit
        * to make sure that the database object
        * is assigned to a CI Super Object's property
        * even if the second parameter is set to TRUE
        *
        */
        if ($return === TRUE)
        {
            $db = 'db_' . $params;
            if (isset($CI->$db)){
                return $CI->$db;
            }
            $CI->$db = DB($params, $active_record);
            return $CI->$db;
        }
        // Initialize the db variable.  Needed to prevent  
        // reference errors with some configurations
        $CI->db = '';
        
        // Load the DB class
        $CI->db =& DB($params, $active_record);    
        
        // Assign the DB object to any existing models
        $this->_ci_assign_to_models();
    }
}

However, this is just a test, not a solution. I would really appreciate if you can help me solve the problem.

p/s: I think it may be better to display the Database Group Name instead of the Database Name in the profiler since Master and Slave databases often have the same name but I currently don't know how.

#7
[eluser]coldfire82[/eluser]
I have the same query of working with 2 databases in CI. Is the problem resolved ? Any good solutions ?

#8
[eluser]sqwk[/eluser]
Tommy Lee's 'solution' works fine for me, however, it is interisting that the Profiler always lists "DATABASE: db-name QUERIES: 1" in the legend tag, even though there is more than one query.

#9
[eluser]WanWizard[/eluser]
I had to modify the _compile_queries method of the profiler library to have multiple DB detection work properly. I replaced
Code:
// Let's determine which databases are currently connected to
foreach (get_object_vars($this->CI) as $CI_object)
{
    if (is_object($CI_object) && is_subclass_of(get_class($CI_object), 'CI_DB') )
    {
        $dbs[] = $CI_object;
    }
}
By
Code:
// Let's determine which databases we are currently connected to
foreach (get_object_vars($this->CI) as $CI_object)
{
    if ( strtolower(get_parent_class($CI_object)) == 'ci_db' )
    {
        $dbs[] = $CI_object;
    }
}

And indeed, the profiler can't access the query statistics if you create the database object locally, the way the manual says it:
Code:
$DB1 = $this->load->database('group_one', TRUE);

But it does if you do
Code:
$this->DB1 = $this->load->database('group_one', TRUE);

You can only access the 'groupname' in the profiler if you modify the CI core file DB.php, since that is where parsing of the parameter takes place (can be anything, from empty to a string to an array)...


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


  Theme © 2014 iAndrew  
Powered By MyBB, © 2002-2021 MyBB Group.