ConnectionManager getDataSource undefined method - cakephp

Using cakephp 2.3.8 I am attempting to setup a connection to a custom couchbase datasource I have made within another datasource. The datasource I am attempting to load functions properly on the rest of the site when being loaded via a model. I want to use the connection manager to load this datasource. This is what I have so far:
database.php file:
public $queriesCB = array(
'datasource' => 'CouchbaseSource',
'username' => 'queries',
'password' => '',
'bucket' => 'queries',
'prefix' => 'q_',
'expiry' => '1814400', //3 Weeks
'autoConnect' => true,
'database' => NULL ,
'persistent' => false
);
Within my other datasource trying to load couchbase datasource
$db = ConnectionManager::getDataSource("queriesCB");
When I debug $db I get this:
object(CouchbaseSource) {
description => 'Couchbase DataSource'
conObject => object(Couchbase) {
[private] _handle => resource
}
config => array(
'password' => '*****',
'database' => '*****',
'prefix' => '*****',
'datasource' => 'CouchbaseSource',
'username' => 'queries',
'bucket' => 'queries',
'expiry' => '1814400',
'autoConnect' => true,
'persistent' => false
)
prefix => 'q__'
connected => false
cacheSources => true
configKeyName => 'queriesCB'
[protected] _baseConfig => array()
[protected] _descriptions => array()
[protected] _sources => null
[protected] _transactionStarted => false
}
Now when I try to call this I get a error:
$db = ConnectionManager::getDataSource("queriesCB");
$db->Get('test');
Error: Call to undefined method CouchbaseSource::Get().
This is a custom method, the datasource is all custom to work best with couchbase and the Get method does function properly. How am I setting up this connection wrong in cakephp?
Edit:
I just tested it with the default database config to a mysql database, it fails as well. The question now would be what would be the best way to initialize a new datasource? should I have to load a model with that datasource attached? Example: have a couchbase model with the datasource as couchbase?
Edit: Here is some of the datasource
class CouchbaseSource extends DataSource {
public $description = 'Couchbase DataSource';
public $conObject = NULL;
public $config = NULL;
public $prefix = NULL;
public function __construct($config = array()){
// If no configuration is set we use the default
$this->config = $config;
// Setup the cache string that is used when building the string
$this->prefix = (isset($this->config['prefix']) ? $this->config['prefix']."_" : "");
if ($this->config['autoConnect']) {
$this->connect();
}
}
public function connect() {
if ($this->conObject !== true) {
try {
$this->conObject = new Couchbase("127.0.0.1:8091", $this->config['username'], $this->config['password'], $this->config['bucket'], $this->config['persistent']);
} catch (Exception $e) {
throw new MissingConnectionException(array('class' => $e->getMessage()));
}
}
return $this->conObject;
}
public function query($method, $params, $object) {
// If not connected... reconnect!
if(!$this->conObject) {
$this->connect();
}
$apiMethod = $this->__methodToClass($method);
if (!method_exists($this, $apiMethod)) {
throw new NotFoundException("Class '{$apiMethod}' was not found");
} else {
return call_user_func_array(array($this, $apiMethod), $params);
}
}
private function __methodToClass($method) {
return 'CB' . strtolower(Inflector::camelize($method));
}
public function describe(&$Model) {
return $this->description;
}
/////////////////////////////////////////////////
// Query Methods
/////////////////////////////////////////////////
public function CBadd($key = NULL, $value = NULL, $expiry = NULL, $persisto = NULL, $replicateto = NULL) {
return $this->conObject->add($key, $value, $expiry, $persisto, $replicateto);
}

The solution is to directly call the query method from the couchbasesource. Given the way that I setup the datasource, unlike linking it through a model, the query information was not automatically passed. My go around was to simple use this.
App::uses('ConnectionManager', 'Model');
$db = ConnectionManager::getDataSource("queriesCB");
debug($db->query('Get', array('test')));

Related

Unit testing the Auth Component

I am using the following code to test the login action in UsersController
public function testLogin() {
$data = array('User' => array(
'username' => 'hello',
'password' => '411'
)
);
$this->Users = $this->generate('Users',array('components'=> array('Auth','Session')));
$this->Users->Auth->staticExpects($this->once())
->method('user')
->with('id');
$this->testAction('/users/login', array('data' => $data, 'method' => 'post'));
}
and the fixture is-
class UserFixture extends CakeTestFixture {
public $import = array('model' => 'User', 'records' => true, 'connection' => 'fixture');
}
adn action is-
public function login() {
if($this->request->is('post')) {
if($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
} else {
return false;
$this->Session->setFlash(__('Wrong Username Or Password,Please Try Again'));
}
}
}
It always showing
Expectation failed for method name is equal to when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
What is the problem?Cant find any solution,and the reason also.Please help.
I think your problem mocking the auth component is that in addition to including auth in your components array, you need to specify which methods of Auth to mock, and how to mock them.
The way I deal with AuthComponent in my tests is I create a superclass with a method: _generateMockWithAuthUserId which mocks the Auth component, among other things, the way I need.
I've pasted the code from my superclass below.
class AppControllerTest extends ControllerTestCase {
public function setUp() {
parent::setUp();
$this->User = ClassRegistry::init('User');
}
public function tearDown() {
unset($this->User);
parent::tearDown();
}
public function testPlaceholder(){
// This just here so we don't get "Failed - no tests found in class AppControllerTest"
$this->assertTrue(true);
}
protected function _generateMockWithAuthUserId($contollerName, $UserId){
$this->authUserId = $UserId;
$this->authUser = $this->User->findById($this->authUserId);
$this->controller = $this->generate($contollerName, array(
'methods' => array(
'_tryRememberMeLogin',
'_checkSignUpProgress'
),
'components' => array(
'Auth' => array(
'user',
'loggedIn',
),
'Security' => array(
'_validateCsrf',
),
'Session',
)
));
$this->controller->Auth
->expects($this->any())
->method('loggedIn')
->will($this->returnValue(true));
$this->controller->Auth
->staticExpects($this->any())
->method('user')
->will($this->returnCallback(array($this, 'authUserCallback')));
}
public function authUserCallback($param){
if(empty($param)){
return $this->authUser['User'];
} else {
return $this->authUser['User'][$param];
}
}
}
And then here's a example of a class that inherits from that superclass. Take note of where/how it calls _generateMockWithAuthUserId. Basically, doing that sets up a suitable controller with Auth mocked for the appropriate user id.
<?php
require_once dirname(__FILE__) . DS . 'AppControllerTest.php';
class EmployeeNotesControllerTestCase extends AppControllerTest {
public $fixtures = array(
// your fixtures go here
);
public function setUp() {
parent::setUp();
$this->EmployeeNote = ClassRegistry::init('EmployeeNote');
}
public function tearDown() {
unset($this->EmployeeNote);
parent::tearDown();
}
public function testSupervisorIndexCanNotSeeNotesOnSelf() {
$authUserId = 1;
$this->_generateMockWithAuthUserId('EmployeeNotes', $authUserId);
$this->controller->Session
->expects($this->once())
->method('setFlash');
$result = $this->testAction('supervisor/employee_notes/index/'.$authUserId, array('return' => 'vars', 'method' => 'get'));
$this->assertTrue(empty($result['employeeNotes']));
}
}
Hope that helps.
I have found a solution.it worked.
public function testLogin() {
$data = array('User' => array(
'username' => 'sasa',
'password' => '111'
)
);
$this->Users = $this->generate('Users', array());
$result = $this->testAction('/users/login', array('data' => $data, 'method' => 'post'));
$this->assertEquals($data['User']['username'],$this->Users->Session->read('Auth.User.username'));
$result = $this->testAction('/users/logout');
}

Multiple Database connection issue CakePHP v2.x

I am using CakePHP v2.x. My current Database is MySQL. My need is to connect to other Database within a method/function written under controller. So what I did so far is add another DB connection array $test in Config/database.php.
class DATABASE_CONFIG {
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'test',
'password' => 'test1',
'database' => 'test_portal',
'prefix' => ''
//'encoding' => 'utf8',
);
public $test = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'dfffd_23',
'password' => 'dsfsd324',
'database' => 'testdbuser',
'prefix' => ''
//'encoding' => 'utf8',
);
}
I need to connect a table named 'aezips'.So I created a new Model;
class Aezips extends AppModel {
public $name = 'Aezip';
public $useDbConfig = 'test';
//public $useTable = 'aezips';
}
In my controller added ;
public $uses = array('Aezip');
Controller having a function/method named add,
public function add() {
$this->layout = NULL;
$this->autoRender = false; //will prevent render of view
if($this->RequestHandler->isAjax()){
Configure::write('debug', 0); //it will avoid any extra output
}
//$this->Aezip->useDbConfig('test');
$t = $this->Aezip->find('all'); //To fetch data from aezips table
print_r($t);
print_r($this->data);
}
But I can't connect to Aezip table and it doesn't shows any error. Both Database residing from same server but different cpanel account.
You seem to have used the wrong name for your Model (plural instead of singular);
class Aezips extends AppModel {}
Should be:
class Aezip extends AppModel {}
And the right filename should be app/Model/Aezip.php
Also, be sure to enable 'debug' inside app/Config/core.php so that you can see the queries that are performed. Also, this will refresh the CakePHP caches more often, which will reduce the risk of using 'cached' Model definitions.
You can debug your variables using debug($variable); in stead of print_r($variable);

Kohana Database instances containts wrong data

I have a problem with Kohana database.
Sometimes I have error
ErrorException [ Recoverable Error ]
Argument 1 passed to Kohana_Database_Query_Builder_Select::compile() must be an instance of Database, string given, called in /srv/sites/mysite/www/modules/database/classes/kohana/database/query.php on line 230 and defined
It's happens because in Database:$instances containts string "dances", but should contain array database configurations.
This my config:
<?php defined('SYSPATH') OR die('No direct access allowed.');
return array
(
'default' => array
(
'type' => 'MySQL',
'connection' => array(
'hostname' => 'localhost',
'database' => 'chat',
'username' => 'root',
'password' => 'root',
'persistent' => FALSE,
),
'table_prefix' => '',
'charset' => 'utf8',
'caching' => FALSE,
'profiling' => TRUE
)
);
Maybe somebody had problem like this or could help me?
Any query to DB causes error.
Like this:
Jelly::factory('user', $user->id())
or this:
DB::select('value')->from('storage')->where('name', '=', 'salt')->limit(1)->execute();
or this:
ORM::factory('node')->where('type', '=', 'page-homepage')->find();
I don't know why this error happen. I checked all methods are called and I have not found any mistakes.
I solved this problem by write method instance in class Database
public static function instance($name = NULL, array $config = NULL)
{
if ($name === NULL)
{
// Use the default instance name
$name = Database::$default;
}
if ( ! is_array(Database::$instances))
{
Database::$instances = array();
}
if ( ! isset(Database::$instances[$name]))
{
if ($config === NULL)
{
// Load the configuration for this database
$config = Kohana::$config->load('database')->get($name);
}
if ( ! isset($config['type']))
{
throw new Kohana_Exception('Database type not defined in :name configuration',
array(':name' => $name));
}
// Set the driver class name
$driver = 'Database_'.ucfirst($config['type']);
// Create the database connection instance
new $driver($name, $config);
}
return Database::$instances[$name];
}
I add condition As you can see
if ( ! is_array(Database::$instances))
{
Database::$instances = array();
}
I don't like this, but I have no choise.
My wild guess would be that you seem to be overwriting the compile, quote, quote_column, quote_identifier, quote_table or execute method from Kohana_Database in your code or simply calling ->execute('dances') should trigger this error.
the compile method shouldn't be called directly, it's an internal function that is called from execute()
Any of these functions take $db as the first parameter. Don't pass anything, since you want to use the database set in your config file, without trying to set it manually in your query builder.

Reading CodeIgniter config values from database instead of config file

As you might know, when you create a new project with CI you'll have to manually enter base url, encryption key in config/config.php. I'm trying to overcome this and is hence looking for a way to read those values from a database instead - making the installation for a customer and the set up time as a whole decrease a lot.
A customer isn't able to edit a PHP file's variables, but is most likely able to, with some guidance, enter base url and have a encryption key automatically filled in by the system.
Is there any way to accomplish this?
Of course! Add a hook - post_controller and set these config values through that file.
config/hooks.php
$hook['pre_controller'][] = array( 'class' => 'MyOtherClass',
'function' => 'MyOtherfunction',
'filename' => 'Myotherclass.php',
'filepath' => 'hooks');
hooks/myotherclass.php
<?
class MyOtherClass {
function MyOtherfunction() {
$CI =& get_instance();
$row = $CI->db->get_where('configs', array('site_id' => 1))->row();
$CI->config->set_item('base_url', $row->base_url);
}
}
Basicly you set these values before they're used in any controller or similiar.
As far Codeigniter 3.1.7 pre_controller won't work because it will return NULL value while initializing $CI =& get_instance();
To solve this:
Change the hook point from pre_controller to post_controller_constructor
Modified source:
config/hooks.php
$hook['post_controller_constructor'][] = array( 'class' => 'MyOtherClass',
'function' => 'MyOtherfunction',
'filename' => 'Myotherclass.php',
'filepath' => 'hooks');
and in hooks/myotherclass.php
<?
class MyOtherClass {
function MyOtherfunction() {
$CI =& get_instance();
$row = $CI->db->get_where('configs', array('site_id' => 1))->row();
$CI->config->set_item('base_url', $row->base_url);
}
}
Now it will work!
class name extends CI_Controller {
public $DB1;
public $MSSQL;
public function __construct()
{
parent::__construct();
$userid = $this->session->userdata('userids');
$this->load->model('kubix');
$result = $this->kubix->databaseconncetion($userid);
foreach ($result as $deta)
{
$this->MSSQL=$db['MSSQL'] = array(
'hostname' => $deta["Db_base_ip"],
'username' => $deta["Db_username"],
'password' => $deta["db_password"],
'database' => $deta["Db_base_name"],
'dbdriver' => 'sqlsrv',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE);
$this->DB1 = $this->load->database($this->MSSQL, TRUE);
$sql = "SELECT TOP 1 * FROM AccountsFinancialYearMaster where Active=1 ";
$query = $this->DB1->query( $sql);
$result= $query->result_array();
RETURN $result;

cakephp dynamic db connection

I have an application where each subscriber gets their own 'app' folder within their own url eg.
www.domain.com/1234 subscriber1
www.domain.com/2345 subscriber2
now the db connection is configured in a class in config/database.php. I want to be able to change the database used based on the url (eg. /1234 = db_1234).
How would I achieve this?
Thanks
BTW I am using CakePHP 2.0
My previous solution was a load of rubbish. So I thought about attacking the problem from a different angle. My DATABASE_CONFIG class (/app/Config/database.php) now looks like this:
class DATABASE_CONFIG {
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => '127.0.0.1',
'login' => 'xxxx',
'password' => 'xxxx',
'database' => 'xxxx',
'prefix' => '',
'encoding' => 'utf8',
);
public $site_one = array(
'datasource' => 'Database/Mysql',
'host' => '127.0.0.1',
// ...
);
public $site_two = array(
'datasource' => 'Database/Mysql',
'host' => '127.0.0.1',
// ...
);
public function __construct()
{
if (strpos(env('HTTP_HOST'), 'site_one') !== false) {
// use site_one database config
$this->default = $this->site_one;
// elseif site_two
} elseif (strpos(env('HTTP_HOST'), 'site_two') !== false) {
// use site_two database config
$this->default = $this->site_two;
}
}
}
When Cake loads, the construct is now called automatically, and this sets the 'default' database connection dependent on the host.
Ah I got it now,
I thought DATABASE_CONFIG was called statically so didn't think to use the construct.
www.domain.com/12345678
function __construct() {
// get folder
if(substr(php_sapi_name(), 0, 3) == "cli") {
$j = explode("/", $_SERVER['PWD']);
$this->default['database'] = "sa_".$j[count($j)-1];
} else {
$j = explode("/", $_SERVER['REQUEST_URI']);
$this->default['database'] = "sa_".$j[1];
}
}
but commandline bake must be done in folder eg. 12345678/

Resources