I got in trouble with Codeigniter 3 using the form validation library; I have to check that the email address submitted by a user into aregistration form is unique into the users database.
I use two databases in my project, the users one is not the default.
To perform the email check I use the following code:
$this->form_validation->set_rules('email','Email','trim|required|valid_email|is_unique[users.email]');
I got an error about the missing users table into the default database, so I've realized that CI 3 looks the email to check into a default database ... not the correct users database, even if in the construct I load the correct model/database.
Is there a way to perform the check into a different database using the form validation above?
Thanks for any feedback
UPDATE
Below the code I use to load the model in the controller
function __construct()
{
parent::__construct();
$this->load->model("admin/user_model","user");
}
Below the code of the User_model
// Database
private $auth_db;
// Tables
private $table_users = 'users';
public function __construct()
{
parent::__construct();
$this->auth_db = $this->load->database('auth', true);
}
and...finally...in the config file the database configuration
$active_group = 'default';
$query_builder = TRUE;
$db['default'] = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => 'xxxxxxxxx',
'password' => 'xxxxxxxxx',
'database' => 'vfr_main',
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => TRUE,
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_unicode_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);
$db['auth'] = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => 'xxxxxxxxxxx',
'password' => 'xxxxxxxxxxx',
'database' => 'vfr_auth',
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => TRUE,
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_unicode_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);
In CodeIgniter 3, models do not have a direct way to control which database group they connect to. In this case I do not think we can simply write a custom validation rule, because is_unique is the only rule that makes a db call, and there is no built in way to change db.
In this case I think the most direct approach would be to extend the form validation class, and then add a new is_unique_authdb method in this extended library for checking with the second db. Then you would use this new method in place of the call you have shown above.
In the 3.x repo on git hub I see this is the existing method.
public function is_unique($str, $field)
{
sscanf($field, '%[^.].%[^.]', $table, $field);
return isset($this->CI->db)
? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
: FALSE;
}
And your extra method could be something like:
public function is_unique_authdb($str, $field)
{
$db = $this->CI->load->database('auth', true);
sscanf($field, '%[^.].%[^.]', $table, $field);
return isset($db)
? ($db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
: FALSE;
}
Extending a native library is very simple. For example, to extend the native Form_validation class you’ll create a file named application/libraries/MY_Form_validation.php, and declare your class with:
class MY_Form_validation extends CI_Form_validation {
public function is_unique_authdb($str, $field)
{
$db = $this->CI->load->database('auth', true);
sscanf($field, '%[^.].%[^.]', $table, $field);
return isset($db)
? ($db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0)
: FALSE;
}
}
Then you would load the library as normal but it will load your extended library and you can use the authdb method for this validation.
$this->form_validation->set_rules('email','Email','trim|required|valid_email|is_unique_authdb[users.email]');
CI3 docs for extending a native library.
Note: With CodeIgniter 4 I think this would be more simple because CI4 has a built in property for models that specifically allows you to manage which db a model will connect to.
Related
I'm just learning some Laravel 9 and i'm doing a simple one to one relationship on two tables. I've created the relationships and the foreign keys which are working fine.
I am trying to create a new Hotel which saves info into the Hotels model and the facilities into the facilities model and are joined by the hotel_id as the foreign key.
I can't quite get my transaction right, where I have the hotel but need to pick up the id for it to pass and also have it as my foreign key on the facilities table.
DB::transaction(function () use ($request) {
$hotel = Hotel::create([
'name' => $request->input('name'),
'address' => $request->input('address'),
'postcode' => $request->input('postcode'),
'state' => $request->input('state'),
'star_rating' => $request->input('star_rating'),
]);
$facility = HotelFacility::create([
'hotel_id' => 39,
'fitness_centre' => true,
'bar' => false,
'bar' => true,
'parking' => true,
'free_wifi' => true,
]);
Hotel::find($hotel->id)->facility()->save($facility);
});
You're doing a few things you don't need to here.
When you call create(), it persists to the Database, so calling ->save() later is redundant.
The last line is completely unnecessary as well.
There's ways to do this code by using relationship methods:
DB::beginTransaction();
try {
$hotel = Hotel::create([
'name' => $request->input('name'),
'address' => $request->input('address'),
'postcode' => $request->input('postcode'),
'state' => $request->input('state'),
'star_rating' => $request->input('star_rating'),
]);
$hotel->facility()->create([
'fitness_centre' => true,
'bar' => false,
'parking' => true,
'free_wifi' => true,
]);
DB::commit();
} catch (Exception $ex) {
DB::rollBack();
}
Good Day!
I have this problem using infinityfree.net as my hosting site.
I used codeigniter framework for my code.
The problem is:
An uncaught Exception was encountered
Type: RuntimeException
Message: Unable to locate the model you have specified: Login_model
My code working properly on xampp.
database.php
$active_group = 'default';
$query_builder = TRUE;
$db['default'] = array(
'dsn' => '',
'hostname' => 'HOSTNAME',
'username' => 'USERNAME',
'password' => 'PASSWORD',
'database' => 'DATABASE_NAME',
'dbdriver' => 'mysqli',
'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
);
Here's my login_model.php
class Login_model extends CI_Model{
public function __construct()
{
parent::__construct();
}
function login($par1,$par2){
//CODE HERE
}
Here's my controller
class Login extends CI_Controller
{
public function __construct()
{
parent::__construct();
$this->load->library('form_validation');
$this->load->helper('url', 'form');
}
public function login()
{
$this->load->model('login_model');
}
PS. i didnt include much more code in controller because thats the only thing that has error and also Im unable to post too much code because of the rule of stackoverflow. Hoping you guys to help me. Thanks in advance
Fixed
I just renamed my login_model to Login_model
In a required scenario, I want to switch to OTHER database dynamically depending on the domain name. I have database-1 in which there is table-1 which stores the hostname, dbname, username and password of the other databases that I need to select and connect dynamically.
I have enabled the 'database' library in autoload.php with the credentials of database-1 in database.php so that when the controller is called, it will check if the OTHER database credentials are already available (in session or some other safezone). If yes, then models will use the already available OTHER database credentials, otherwise SELECT query should be fired on database-1 to fetch the OTHER database credentials and then the models should use the OTHER database credentials.
I have two issues in my implementation:
I don't know the condition where to store and check OTHER database credentials availability. If I save it in a session, OTHER db credentials might be hacked by session hijacking. Also the credentials will be fetched again and again for every visitor of a domain.
CI does not reconfigure the 'database' library or 'models' to work on fetched OTHER database credentials, it still uses the autoloaded database-1 credentials from database.php.
$this->load->model('otherdb');
$otherconfig=$this->otherdb->getotherdb(base_url());
$config=array();
if(isset($otherconfig) && !empty($otherconfig)){
$config['hostname'] = $otherconfig->host;
$config['username'] = $otherconfig->user;
$config['password'] = $otherconfig->pass;
$config['database'] = $otherconfig->nameofdb;
$config['dbdriver'] = 'mysqli';
$config['dbprefix'] = '';
$config['pconnect'] = FALSE;
$config['db_debug'] = TRUE;
}
$this->load->database($config); //doesnt work, still uses old DB
$this->load->model('model1','',$config); //doesnt work, still uses old DB
$this->load->model("model2",'',$config); //doesnt work, still uses old DB
$this->load->model('model3','',$config); //doesnt work, still uses old DB
Connect to the database in your Model rather than in the controller, below approach allows you to use 2 database at the same time. It also helps to keep the database stuff in the model and the controller clear of it.
in your controller, remove the config settings and just load the model like:
$this->load->model('Model1');
$this->Model1->getdata(); // call a function in your model
then in your model you do the db configuration
class Model1 extends CI_Model {
function __construct()
{
parent::__construct();
$otherconfig=$this->getotherdb(base_url());
$config=array();
if(isset($otherconfig) && !empty($otherconfig)){
$config['hostname'] = $otherconfig->host;
$config['username'] = $otherconfig->user;
$config['password'] = $otherconfig->pass;
$config['database'] = $otherconfig->nameofdb;
$config['dbdriver'] = 'mysqli';
$config['dbprefix'] = '';
$config['pconnect'] = FALSE;
$config['db_debug'] = TRUE;
$this->db2=$this->load->database($config, true);
$this->db=$this->load->database('', true); //load the 'default' database as defined in your config/database.php
}
function getdata()
{
$db2->query();
$db2->result();
}
function getotherdb($base_url)
{
// get your database credentials
// you can still use the autoloaded db
$db->query();
$db->result();
}
}
from the docs: Connecting to Multiple Databases
Depending on what you want to do the application of the logic may be different but with that being said you can configure multiple Database connections in CI in your database.php file.
$db['default'] = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => 'root',
'password' => '',
'database' => 'mydatabase',
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => TRUE,
'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
);
$db['second'] = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => 'root',
'password' => '',
'database' => 'mydatabase',
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => TRUE,
'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
);
To use the second database connection in your controllers you can simply select it using this:
private $db2;
function __construct(){
parent::__construct();
$db2 = $this->load->database('second', TRUE);
// YOU NEED TO ADD TRUE AS THE SECOND PARAMETER TO LOAD THE DB OBJECT
}
public function Users(){
$query = $this->db2->get('TABLE_NAME');
return $query->result_array();
}
If you are loading the database configuration from an initial DB this can still be done, don't forget that codeigniter is still PHP.
The $db[] array can be created dynamically using logic, you could create a json file that stores such information and populate the db array with it.
In my site are different modules, each has its own datatable.
So now I'm not sure which is the best way to connect these modules in the best way.
My idea is to create a helper function, which tests if is a database-connection is available or not. If not, the helper should init the database and make the database for queries available in the controller and models.
In the documentation of Codeigniter I've only found information about multiple database setup - I cannot find an example for do that with a kind of dynamic helper.
Maybe someone can help me a step further?
What you can do is set session of your db_name, db_user and db_pass (if user and pass are different for each db_name). In order to reuse the dynamic db without always passing the access.
Here is an implementation:
Helper:
if (!function_exists('get_dynamic_db')){
function get_dynamic_db()
{
$CI =& get_instance();
$db = $CI->session->user_data('other_db');
$user = $CI->session->user_data('other_db_user');
$pass = $CI->session->user_data('other_db_pass');
$config_app = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => $user,
'password' => $pass,
'database' => $db,
'dbdriver' => 'mysqli',
'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
);
return $CI->load->database($config_app,TRUE);
}
}
Model:
Class DD_model extends CI_Model {
var $dynamic_db;
public function __construct() {
$this->load->database(); // default db
$this->dynamic_db = get_dynamic_db(); // dynamic db
}
public function ping_server_db()
{
$this->dynamic_db->from('some_table');
$query = $dynamic_db->get();
return $query->row() ? true : false;
}
}
Controller:
public function select_db($db_name)
{
$this->session->set_userdata(array('other_db' => $db_name, 'other_db_user' => 'user', 'other_db_pass' => 'pass'));
$dynamic_db = $this->DD_model->ping_server_db();
if (!$dynamic_db) {
$this->session->unset_userdata('other_db');
return false;
}
}
With this, you can then use get_dynamic_db(); in all your models to query from dynamic db
I've created custom doctrine(1.2) behavior which should create tables for models (very simmilar to i18n behavior). I see this tables in schema.sql, and if i execute it everything is fine, but this is no such tables if my migrations diff (doctrine:generate-migrations-diff).
What i'm doing wrong?
class DescriptionableGenerator extends Doctrine_Record_Generator
{
protected $_options = array(
'className' => '%CLASS%Description',
'tableName' => '%TABLE%_description',
'fields' => array(),
'generateFiles' => false,
'table' => false,
'pluginTable' => false,
'children' => array(),
'options' => array(),
'cascadeDelete' => true,
'appLevelDelete' => false
);
public function __construct(array $options = array())
{
$this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options);
}
public function buildRelation()
{
$this->buildForeignRelation('Descriptions');
$this->buildLocalRelation();
}
public function setTableDefinition()
{
$this->hasColumn('lang', 'char', '2', array('notnull' => true));
$this->hasColumn('field', 'string', '255', array('notnull' => true));
$this->hasColumn('title', 'string', '255', array('notnull' => true));
$this->hasColumn('description', 'clob');
$this->hasColumn('compulsory', 'boolean', 1, array('notnull' => true, 'default' => 0));
$this->addListener(new DescriptionableListener());
}
}
Solved!
Problem appears due to command "php symfony doctrine:build-model".
So, if you have the same problem you should:
Remove your behavior from schema.
Execute "php symfony doctrine:build-model".
Add your behavior to schema.
Run "php symfony doctrine:generate-migrations-diff".
Chears! %)