Reading CodeIgniter config values from database instead of config file - database

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;

Related

Array to DB::insert()->values(), which is in different order with the columns

Hi folks! I'm trying to transfer data as array from the controller to the model, and then paste the data into the query builder, but the data must be in the same order as specified in the columns.
What options do I have?
And do you think this is a bad practice?
Controller:
$responseNotes = Model::factory('Notes')-> createTicket([
'description' => htmlspecialchars($_POST['description']),
'contact_id' => $_POST['contact_id'],
'pref_contact' => $_POST['pref_contact'],
'dog_id' => $_POST['document_id'],
'type' => $_POST['type'],
'owner_id' => Auth::instance()->get_user()->id,
'cc' => $_POST['cc-emails'],
'title' => $_POST['title']
]);
Model:
public function createNote(array $data)
{
$columns = [
'type',
'owner_id',
'cc',
'title',
'description',
'contact_id',
'pref_contact',
'dog_id'
];
if (!array_diff($columns, array_keys($data))) {
// All needed values exists
$result = DB::insert($this->NOTES, $columns)-> values($data)-> execute($this->SCHEMA);
}
return ($result) ? $result : false ;
}
Thanks to this answer. Solved this by:
// Order $data array according to $column.values
$orderedData = [];
foreach ($columns as $key) {
$orderedData[$key] = $data[$key];
}
$result = DB::insert($this->TICKETS, $columns)
-> values($orderedData)
-> execute($this->SCHEMA);
Why you don't use ORM Model?
in controller:
$responseNotes = ORM::factory('Notes')-> values([
'description' => htmlspecialchars($_POST['description']),
'contact_id' => $_POST['contact_id'],
'pref_contact' => $_POST['pref_contact'],
'dog_id' => $_POST['document_id'],
'type' => $_POST['type'],
'owner_id' => Auth::instance()->get_user()->id,
'cc' => $_POST['cc-emails'],
'title' => $_POST['title']
])
try{
$responseNotes->save();
} catch (ORM_Validation_Exception $ex) {
print_r($ex->errors('models'));
}
And don't use htmlspecialchars($_POST['description'])
In model class modify function (doc):
public function filters()
{
return array(
'description' => array( array('htmlspecialchars') ),
);
}
It looks like You have associative array with structure db_column=>value right? Than You can simply insert like this:
DB::Insert('table_name',array_keys($data))->values(array_values($data))->execute();

rawurlencode() expects parameter 1 to be string, array given [CORE\Cake\Routing\Route\CakeRoute.php, line 506]

I am new in CakePHP. I wanted to create menu in cakePHP 2.3.9 but when I run the application it gives following warning "rawurlencode() expects parameter 1 to be string, array given [CORE\Cake\Routing\Route\CakeRoute.php, line 506]"
I am giving the complete code here. I can not understand how to eliminate the warning message above.
At first I have created this file at /app/Model/menu.php. Which looks like this
<?php
class Menu extends AppModel {
var $name = 'Menu';
var $useTable = false;
function main ($selected = 'home') {
return array(
'divClass' => 'menu',
'ulClass' => 'menu',
'tabs' => array(
array(
'controller' => 'pages',
'action' => '',
'params' => '',
'aClass' => $selected == 'home' ? 'selected' : '',
'liClass' => '',
'text' => 'Home'
),
array(
'controller' => 'pages/service',
'action' => '',
'params' => '',
'aClass' => $selected == 'Service' ? 'selected' : '',
'liClass' => '',
'text' => 'Service'
),
array(
'controller' => 'pages/user',
'action' => '',
'params' => '',
'aClass' => $selected == 'users' ? 'selected' : '',
'liClass' => '',
'text' => 'Users'
),
)
); // end return
} // end Main
} // end class Menu
?>
Then I have created this file at /app/Controller/MenusController.php. Which looks like this
<?php
class MenusController extends AppController {
var $name = 'Menus';
private $menus;
function beforeFilter () {
parent::beforeFilter();
$this->menus[] = array(
'name' => 'main',
'selected' => 'users'
);
// $this->set('menuList', $this->menus);
}
function index() {
$this->menus[] = array(
'name' => 'users',
'selected' => 'users'
);
$this->set('menuList', $this->menus);
}
function menus($menus) {
$output = array();
foreach ($menus as $menu):
$output[] = $this->Menu->{$menu['name']}($menu['selected']);
endforeach;
return $output;
}
}
?>
Then, this file I have created at /app/View/Elements/menu.ctp, which looks like this
<?php
if (empty($menuList)):
$menuList = array(
array(
'name' => 'main',
'selected' => 'home'
)
);
endif;
$menus = $this->requestAction(
array(
'controller' => 'menus',
'action' => 'menus'
),
array('pass' => array( $menuList))
);
if (! empty($menus)):
foreach ($menus as $menu):
$tabs = '';
foreach ($menu['tabs'] as $tab):
$url = array(
'controller' => $tab['controller'],
'action' => $tab['action']
);
if (! empty($tab['params'])):
$url[] = $tab['params'];
endif;
$tabs .= $this->html->tag(
'li',
$this->html->link(
$tab['text'],
$url,
array('class' => $tab['aClass'])),
array('class' => $tab['liClass'])
);
endforeach;
echo '<div class="' . $menu['divClass'] . '">
<div class="container_12">
<div class="grid_12">' . $this->html->tag('ul', $tabs, array('class' =>
$menu['ulClass'])) . '</div></div></div>';
endforeach;
endif;
?>
accept my advance gratitude for help
Your framework might have a default helper function which uses php's rawurlencode() function by default too. You might want to check the parameters you should pass for $this->html->link() since php's rawurlencode() function accepts only a string as a parameter.
Although the documentation says requestAction now supports 'array based cake style URLs' which 'bypass the usage of Router::url()', try passing a string as the first parameter to request action.
For example where you currently have:
array(
'controller' => 'menus',
'action' => 'menus'
),
Try:
'menus/menus',

cakephp 2.2 Using contain with Auth not working

I have a working example of Auth set up with Users & Groups, based on the cookbook tutorial. I have an additional Locations table, which is associated with my Groups.
Location hasMany Group. Group belongsTo Locations. In 2.2 I think I should be able to get location data back for a User, but am not able to.
// App controller
public function beforeFilter() {
$this->Auth->authenticate = array(
'all' => array (
'scope' => array('User.status' => 1)
),
'Form' => array(
'contain' => array('Group', 'Location'),
'fields' => array('Location.name')
)
);
The above code only works if I create a direct association between User and Location. Is it possible to use contain here with a Group to Location association?
Problem of BaseAuthenticate is how it returns user info: return $result[$model];.
So when I need contains I'm using alternative component placed in app/Controller/Auth:
App::uses('FormAuthenticate', 'Controller/Component/Auth');
class FormAndContainableAuthenticate extends FormAuthenticate {
protected function _findUser($username, $password) {
if (empty($this->settings['contain'])) { //< deafult
$userData = parent::_findUser($username, $password);
} else { //< with contains
$userModel = $this->settings['userModel'];
list($plugin, $model) = pluginSplit($userModel);
$fields = $this->settings['fields'];
$conditions = array(
$model . '.' . $fields['username'] => $username,
$model . '.' . $fields['password'] => $this->_password($password),
);
if (!empty($this->settings['scope'])) {
$conditions = array_merge($conditions, $this->settings['scope']);
}
$modelObj = ClassRegistry::init($userModel);
$modelObj->contain($this->settings['contain']);
$result = $modelObj->find('first', array(
'conditions' => $conditions
));
if (empty($result) || empty($result[$model])) {
return false;
}
foreach($result as $modelName => $modelData) {
if ($modelName !== $model) {
$result[$model][$modelName] = $modelData;
}
}
$userData = $result[$model];
}
// remove dangerous fields like password
unset($userData[$this->settings['fields']['password']]);
if (!empty($this->settings['exclude'])) {
foreach ($this->settings['exclude'] as $fieldName) {
unset($userData[$fieldName]);
}
}
return $userData;
}
}
As you can see - it uses parent Component when no contains provided.
Also some bonus: you can provide a set of fields to remove from resulting array. Just pass field names via 'exclude' key
How to use Component:
public $components = array(
'Auth' => array(
'authenticate' => array(
'FormAndContainable' => array(
'fields' => array(
'username' => 'username',
'password' => 'password',
),
'userModel' => 'Staff',
'contain' => array('StaffPermission'),
'exclude' => array('plain_password')
)
),
),
);
If User is not linked to Location, but Group is, you should try this:
'contain' => array('Group' => array('Location')),

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/

How Can I Read the DB Configuration Settings From a Cake Shell?

I would like to write a cake shell to do a nightly backup of my database using mysqldump. I could do this as a shell script, but if I can cram it into a CakePHP shell, then I will get the added benefit of it working across both the development and live server, if I can get it to automagically read my database config settings. I will cron the cake shell and have some peace-of-mind knowing that I have frequent backups of my DB.
In my shell I'm trying to build up a string which starts with "mysqldump --user=" and I'd like to get the username from app/config/database.php. How can I get at the data in database.php?
In cake 2.1 the format has changed to:
App::uses('ConnectionManager', 'Model');
$dataSource = ConnectionManager::getDataSource('default');
$username = $dataSource->config['login'];
The following snippet should do the trick:
App::import('Core', 'ConnectionManager');
$dataSource = ConnectionManager::getDataSource('default');
$username = $dataSource->config['login'];
In CakePHP 3.x the format has changed to -
use Cake\Datasource\ConnectionManager;
$source = ConnectionManager::get('default');
debug($source); #Debugging the result
Result :
object(Cake\Database\Connection) {
'config' => [
'password' => '*****',
'username' => '*****',
'host' => '*****',
'database' => '*****',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
'log' => false,
'url' => null,
'name' => 'remote'
],
'driver' => object(Cake\Database\Driver\Mysql) {
'connected' => false
},
'transactionLevel' => (int) 0,
'transactionStarted' => false,
'useSavePoints' => false,
'logQueries' => false,
'logger' => null
}
Get the Result :
debug($source->config()); #Accessing the result
Result :
[
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => 'username',
'password' => 'password',
'database' => 'database',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
'log' => false,
'url' => null,
'name' => 'remote'
]
Just for sharing.
For any cakephp project, if using php as cronjob or command line to do large data processing I would build a standalone php script without cakephp, the reason for doing this because cakephp is slow (e.g. read & process 100K records).
To make my life simple I put all my standalone scripts under app/Vendor/myscripts/ (e.g: app/Vendor/myscripts/process.php)
below also the basic code to make sure you use the same database settings in standalone script with cakephp (tested with MySQL)
require_once '/XYZ/app/Config/database.php';
$database = new DATABASE_CONFIG;
try{
$dblink = new PDO('mysql:host='.$database->default['host'].';dbname='.$database->default['database'], $database->default['login'], $database->default['password'], array(PDO::ATTR_PERSISTENT => false));
$dblink->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dblink->exec('SET CHARACTER SET '.$database->default['encoding']);
} catch (Exception $e) {
die('DB Error'. $e->getMessage());
}
Example in controller, Change multi DB for DataSources in CakePHP 2.5.x
App::uses('AppController', 'Controller');
class DemoController extends AppController {
public $uses = array('AppModel', 'GVA21', 'GVA01', 'GVA14', 'GVA24' );
public function test_dbs(){
$this->autoRender=false;
// Load ConnectManager
App::uses('ConnectionManager', 'Model');
// DataSource ['default']
$MDM = $this->GVA14->find('count');
echo "MDM.GVA14\n<br>";
debug($MDM);
// Get DataSource Config DB ['default'] and ['SRL']
$confDeafult = ConnectionManager::$config->default;
$confSrl = ConnectionManager::$config->SRL;
// Change DataSource ['SRL']
ConnectionManager::drop('default');
ConnectionManager::create('default',$confSrl); //<== Is permanet change Find All models Down
// $this->GVA01->setDataSource('SRL'); //<== Is temp change Find model
echo "SRL.GVA14\n<br>";
$SRL = $this->GVA14->find('count');
debug($SRL);
$SRL = $this->GVA01->find('count');
echo "SRL.GVA01\n<br>";
debug($SRL);
$SRL = $this->GVA21->find('count');
echo "SRL.GVA21\n<br>";
debug($SRL);
// Change to DataSource ['default']
debug(ConnectionManager::drop('default'));
ConnectionManager::create('default',$confDeafult); //<== Is permanet change Find All models Down
//$this->GVA01->setDataSource('default'); //<== Is temp change Find model
$MDM = $this->GVA01->find('count');
echo "MDM.GVA01\n<br>";
debug($MDM);
$MDM = $this->GVA21->find('count');
echo "MDM.GVA21\n<br>";
debug($MDM);
////FIN
exit('FIN');
}

Resources