Codeigniter Transactions - php

I'm using Codeigniter transactions
$this->db->trans_start();
$this->db->query('AN SQL QUERY...');
$this->db->trans_complete();
This works fine , the problem I have is that inside the trans_start and trans_complete I'm calling other functions and those functions deals with database so they contains inserts and update and some deletes ... ex:
$this->db->trans_start();
$this->utils->insert_function($data);
$this->utils->update_function2($test);
$this->db->trans_complete();
Now if those functions are executed and some errors occur CodeIgniter won't do a rollback.
What is the best way to deal with such issue?
The only solution, I have in mind, is to return an error from those functions and inside those function add (trans_stat and trans_complete) And if it returns an error test an do $this->db->trans_rollback
ex:
$this->db->trans_start();
$result = $this->utils->insert_function($data);
if($result === false){
$this->db->trans_rollback();
}
$this->db->trans_complete();
Is there a better way of doing this?
Update 1:
As requested a sample of the external function i'm calling :
// insert_function contains
$rec = array(
'numero' => $numero,
'transaction_id' => $id,
'debit' => $product_taxes['amount_without_taxes'],
'date' => $data['date_transaction'],
);
$this->addExerciceAccountingRecords($rec);
and addExerciceAccountingRecords contains
function addExerciceAccountingRecords($records) {
$this->db->insert('transactions_exercices', $records);
}

Using transactions means support databases to insert data safely. So in Codeigniter we write every database related functions in the Model not in Controller.. And in your second code(which is not working)you have pointed model on there.(utils). So simple I'm sure this will not work. Because its not a insert data with model and Controller parallel. Transaction should be coded in the Model(I will write in Model in my answer).
Load this stuffs as well
Database Library
Model Class
URL helper
Session
Assumptions
In your code you have used $data and $test as array. So i assume there is two array for inserting and updating data.
Your data sets
$data = array(
'title' => 'My title' ,
'name' => 'My Name' ,
'date' => 'My date'
);
$id = 007;
$test = array(
'title' => $title,
'name' => $name,
'date' => $date
);
Your Code
$this->db->trans_start(); # Starting Transaction
$this->db->trans_strict(FALSE); # See Note 01. If you wish can remove as well
$this->db->insert('table_name', $data); # Inserting data
# Updating data
$this->db->where('id', $id);
$this->db->update('table_name', $test);
$this->db->trans_complete(); # Completing transaction
/*Optional*/
if ($this->db->trans_status() === FALSE) {
# Something went wrong.
$this->db->trans_rollback();
return FALSE;
}
else {
# Everything is Perfect.
# Committing data to the database.
$this->db->trans_commit();
return TRUE;
}
Notes
By default Codeigniter runs all transactions in Strict Mode. When
strict mode is enabled, if you are running multiple groups of transactions, if one group fails all groups will be rolled back. If
strict mode is disabled, each group is treated
independently, meaning a failure of one group will not affect
any others.

What I tried was more of a trick, but it worked for me.
$this->db->trans_begin();
$rst1= $this->utils->insert_function($data);
$rst2 = $this->utils->update_function2($test);
if($this->db->trans_status() === FALSE || !isset($rst1) || !isset($rst2)){
$this->db->trans_rollback();
}else{
$this->db->trans_commit();
}

I suspect the problem has to do with how CodeIgniter is handling objects.
If you go to the CI documentation under the section "Creating Libraries" at:
http://ellislab.com/codeigniter/user-guide/general/creating_libraries.html
and look at the section related to:
$CI =& get_instance();
$CI->load->helper('url');
$CI->load->library('session');
$CI->config->item('base_url');
In your main controller, you have loaded/instantiated the database class either using auto load or explicitly loading the class.
You then go ahead and open the transaction, and then, you access your database
functions through your utils library.
However, once you use $this-db in your library, you are actually accessing another copy of the database instance, NOT the one that is associated with your transaction.
To access the same instance, you need to use the get_instance() function.
I think that should fix your problem. Your original coding style to separate function
into various modules is excellent. You simply need to understand this additional detail.
Please try and confirm that the roll back works as you expect.
The guts of the code consists of the following controller:
$this->db->trans_start();
$this->User_profile_m->create_new_user_profile();
$this->User_profile_m->create_new_user();
$this->db->trans_complete();
and a simple model user_profile_m to deal with data persistence:
function create_new_user()
{
$data['user_name_usr'] = $this->input->post('user_name');
$data['create_date_usr'] = NULL;
$this->db->insert('user_usr', $data);
}
function create_new_user_profile()
{
$data['user_name_pro'] = $this->input->post('user_name');
$data['user_description_pro'] = $this->input->post('user_description');
$data['create_date_pro'] = NULL;
$this->db->insert('user_profile_pro', $data);
}
Essentially, the demonstration tries to do two inserts (one in each of two tables).
If one insert fails, the other is rolled back.
I built this in CodeIgniter 2.1.3 and I can make the application files available through GitHub or zip them up and send them to you.

Try this procedure. It really work for me :)
$this->db->trans_start();
$this->utils->insert_function($data);
$this->utils->update_function2($test);
if($this->db->trans_status() === FALSE){
$this->db->trans_rollback();
}else{
$this->db->trans_complete();
}

Note: Make sure to use $this->db->trans_begin() when running manual transactions, NOT $this->db->trans_start().
$this -> db -> trans_begin();
$this -> utils -> insert_function ( $data );
$this -> utils -> update_function2 ( $test );
$this -> db -> trans_complete ();
Certify in case use MySql, use in InnoDb Format

For single insert or update record you can use affected_rows function
$this->db->insert('table_name', xss_clean($data));
//Check if there is a record affected
if($this->db->affected_rows() > 0)
{
return true;
}
else
{
// if not succeeded
// check your last query
die($this->db->last_query());
}

Related

how to give to the user a feedback message after "save" function codeigniter

normally I would return a JSON with a message about the status of my function, if error or success, but, this time, I'm with a trouble. I'm developing an application with PHP to register students to join the university, the database has a lot of tables to insert data about the student, things like, parents, student address, student course, course value and much more, so, after the user fill all the form fields and press submit, I do a lot of insert and updates, like 10 inserts in differents tables, my question is, how I can return a message of status after all this operation, if one of my inserts fail, how I can handle it to give to the user a feedback about this? I'm a little new to CodeIgniter, here one example of how I do my inserts in my save function at controller:
$studentData = array(
'FIELD1' => $data1,
'FIELD2' => $data2,
'FIELD3' => $data3
);
$this->mymodel->insert('STUDENTTABLE', $data);
I do so many inserts like this above. how can I return a feedback of every insert and at final of my save function to return a success message?
There are many techniques to know that you inserted successfully or not.
You can use this $this->db->affected_rows() function to know after query it will return you affected row. you can simply use if condition to it to check.
return ($this->db->affected_rows() != 1) ? false : true;
$this->db->insert_id(); this function will return you the last inserted id so depends on that you can do further coding.
The Best Way to find the query execution result is Transactions. I am not adding much description here because you can find it in the user guide of CodeIgniter. Here is the link. I am just adding an example of it so you can get the little idea of it.
$this->db->trans_begin();
// Your query
if ($this->db->trans_status() === FALSE)
{
$this->db->trans_rollback();
}
else
{
$this->db->trans_commit();
}
I personally prefer the transaction for insert and update query. It is very useful if you are adding multiple data or doing more than one activity regarding the database.
Now For Passing the Message to the View.
If you use the above technique and return true or false from model to controller then it would be easy to get message according to that.
I am adding sample model, controller and view code for you.
public function your_model_function(){
// Do query
// Return TRUE or FALSE
}
public function your_controller_function(){
$data = $this->input->post(); // Your post data. You can use get also.
$result = $this->your_model->your_model_function($data);
//Normal Pass the variable to the view [OPTION-1]
if($result == TRUE){
$msg = "Successfully Insert.";
}else {
$msg = "Failed To Insert.";
}
$this->load->view('your_view',['msg'=>$msg]); // Change this code as per your requierment.
//Now For Flash Data [OPTION-2]
if($result == TRUE){
$this->session->set_flashdata('feedback', 'Successfully Insert.');
}else {
this->session->set_flashdata('feedback', 'Failed To Insert.');
}
//redirect to your page
return redirect('your_controller/your_controller_function'); // Change this code as per your requierment.
}
View For [Option-1]
<?php echo $msg; ?>
//OR
<?php print_r($msg); ?>
View For [Option-2]
<?php
if ($feedback = $this->session->flashdata('feedback')){
echo $feedback;
}
?>
// Set flash data in your controller
$this->session->set_flashdata('message_name', 'This is my message');
// After that you need to used redirect function instead of load view such as
redirect("admin/signup");
// Get Flash data on view in view page
$this->session->flashdata('message_name');
For more reference, you can visit:this
tutorialspoint

Multiple queries in laravel scope function

I am attempting to make multiple queries in a scope function in laravel. my code is as follows. the first query executes normally but the second seems to be ignored. what is the correct way to do this?
public function scopeUpdateStatus($query,$oldUser, $newUser, $alias, $env) {
$query->where('db_conn_app_alias_user', $newUser)->where('db_conn_app_alias', $alias)->where('app_instance_environment', $env)->update(array('user_status' => 'active'));
$query->where('db_conn_app_alias_user', $oldUser)->where('db_conn_app_alias', $alias)->where('app_instance_environment', $env)->update(array('user_status' => 'passive'));
return "success";
}
The trick here is to use with (a Laravel helper function) and clone.
function scopeSomeName($query) {
with(clone $query)->whereStuff;
with(clone $query)->whereOtherStuff;
}
This happens because you use the same $query variable in the two updates. You add where()s to the $query in the first update query and then run it, but when you add your where()s in the second update query the where()s from the first query are still there. Because of this your query will return zero result so there is nothing to update. Copy the $query first to a new variable then run the second query in the copied one:
public function scopeUpdateStatus($query, $oldUser, $newUser, $alias, $env) {
$queryTemp = $query;
$query->where('db_conn_app_alias_user', $newUser)
->where('db_conn_app_alias', $alias)
->where('app_instance_environment', $env)
->update(array('user_status' => 'active'));
$queryTemp->where('db_conn_app_alias_user', $oldUser)
->where('db_conn_app_alias', $alias)
->where('app_instance_environment', $env)
->update(array('user_status' => 'passive'));
return "success";
}
A minor edit (reduces overhead) to #merlinpatt answer. You don't need to clone the $query twice. Just once, as you already have the existing/original variable
function scopeSomeName($query) {
$query_cloned = clone $query;
$query->whereStuff;
$query_cloned->whereOtherStuff;
}
Also, there's no need for the with() helper function.
Tested it and works as expected.

CodeIgniter: return FALSE if Active Record can't find any data

Using CodeIgniter, I find the following code in all the models that gathers data from a database:
// .. taken from function get_user_data($user_id)
// Select data
$user_data = $this->db->from('users')->where('id', $user_id)->get()->row();
// Check if we got any matches
if(isset($user_data->id)) {
// Indeed we did, return the data found
return $user_data
} else {
// Nope, no data found
return FALSE;
}
The interesting part is where I check if the query actually returned any data. I'm doing that for EVERY query, which adds up to quite a bit repetitive code.
Is there any way to, perhaps override the CodeIgniter functions, making them return FALSE if no data was found?
I'm probably missing something, as I can't see why CodeIgniter isn't handling this already.
There isn't much in the way of built in shortcuts. Even the manual suggests checking your results:
If you run queries that might not produce a result, you are encouraged to test the result first:
$query = $this->db->query("YOUR QUERY");
if ($query->num_rows() > 0)
{
// found results
}
You could always use a class extension:
class MY_Model extends CI_Model {
protected function _get_row($result)
{
return $result->num_rows() ? $result->row() : FALSE;
}
}
Usage in a model:
function get_user_data($user_id)
{
$user_data = $this->db->from('users')->where('id', $user_id)->get();
return $this->_get_row($user_data);
}
You'd just have to extends MY_Model for the models you want to have access to the function.
Another option would be to return the result of $this->db->get() instead, and do the check in your controller (which you would probably have to do anyways).
I agree with wesley murch option but i think creating a entire class for an individual function isn't good practice. My opinion is to use helpers. You can try this:
In Helper File:
function get_db_data($result)
{
return ( $result->num_rows() > 0 ) ? $result->result_array() : false;
}
You can call this function in any of your model with
$this->load->helper('helper_file_name');
$dbData = get_db_data(result_object);

Determining which field causes Doctrine to re-query the database

I'm using Doctrine with Symfony in a couple of web app projects.
I've optimised many of the queries in these projects to select just the fields needed from the database. But over time new features have been added and - in a couple of cases - additional fields are used in the code, causing the Doctrine lazy loader to re-query the database and driving the number of queries on some pages from 3 to 100+
So I need to update the original query to include all of the required fields. However, there doesn't seem an easy way for Doctrine to log which field causes the additional query to be issued - so it becomes a painstaking job to sift through the code looking for usage of fields which aren't in the original query.
Is there a way to have Doctrine log when a getter accesses a field that hasn't been hydrated?
I have not had this issue, but just looked at Doctrine_Record class. Have you tried adding some debug output to the _get() method? I think this part is where you should look for a solution:
if (array_key_exists($fieldName, $this->_data)) {
// check if the value is the Doctrine_Null object located in self::$_null)
if ($this->_data[$fieldName] === self::$_null && $load) {
$this->load();
}
Just turn on SQL logging and you can deduce the guilty one from alias names. For how to do it in Doctrine 1.2 see this post.
Basically: create a class which extends Doctrine_EventListener:
class QueryDebuggerListener extends Doctrine_EventListener
{
protected $queries;
public function preStmtExecute(Doctrine_Event $event)
{
$query = $event->getQuery();
$params = $event->getParams();
//the below makes some naive assumptions about the queries being logged
while (sizeof($params) > 0) {
$param = array_shift($params);
if (!is_numeric($param)) {
$param = sprintf("'%s'", $param);
}
$query = substr_replace($query, $param, strpos($query, '?'), 1);
}
$this->queries[] = $query;
}
public function getQueries()
{
return $this->queries;
}
}
And add the event listener:
$c = Doctrine_Manager::connection($conn);
$queryDbg = new QueryDebuggerListener();
$c->addListener($queryDbg);

How to find query executed successfully or not?

im using codeigniter 2.0.2 and this is from its userguide
$data = array(
'title' => $title,
'name' => $name,
'date' => $date
);
$this->db->where('id', $id);
$this->db->update('mytable', $data);
my question is once this executed how do you find its executed correctly or not?
The update function returns a value:
$result = $this->db->update('mytable', $data);
Check that value for either being TRUE (success) or FALSE (failure). update runs query internally and then returns the return value of query (Ref):
The query() function returns a database result object when "read" type queries are run, which you can use to show your results. When "write" type queries are run it simply returns TRUE or FALSE depending on success or failure.
Use
$this->db->affected_rows()
to see how many rows have been affected on write type queries (update, insert, etc...)
http://codeigniter.com/user_guide/database/helpers.html
Both answers was valid. You just have to use each one depending on the case. If you are just checking if the query was executed use this method:
$result = $this->db->update('mytable', $data);
if you really want the number of rows affected the second method is better:
$this->db->affected_rows()
however I always use the second method. The update query is a good example why. A query can be successful and still there was nothing updated on the database because the value that you were trying to update was actually equal to the value you are sending in the query.
This would be a false positive. And the affected rows would be 0.
I hope it helped =)
When developing CodeIgniter model methods, I find that I consistently return desirable values depending on the type of database write that is executed. It is often important to differentiate between a query that has run successfully versus a query that has actually changed a record.
For an update or delete query, I'll return the number of affected rows -- this will be most helpful to controller methods that call it. If you are performing logging (to keep track of change history), then ONLY log something if there is a change to the row; otherwise you are unnecessarily bloating your change history logs.
public function update(int $id, array $newData) :int
{
$oldData = $this->db->get_where('mytable', ['id' => $id])->row_array();
if ($this->db->update('mytable', $newData, ['id' => $id])) {
$affectedRows = $this->db->affected_rows();
if ($affectedRows) {
$this->Log->mytableUpdate($id, $newData, $oldData);
}
return $affectedRows;
}
return 0;
}
For insert queries, I always return the auto-incremented id of the newly inserted row via insert_id().
If using the PDO driver with PostgreSQL, or using the Interbase driver, this function requires a $name parameter, which specifies the appropriate sequence to check for the insert id.
public function insert(array $newData) :int
{
if ($this->db->insert('mytable', $newData)) {
$newId = $this->db->insert_id(); // or insert_id('mytable')
$this->Log->mytableInsert($newId, $newData);
return $newId;
}
return 0;
}
Having consistent return types in your model methods will make your project easier to develop and maintain. The script that calls these model methods will be able to quickly assess the outcome by making a "falsey" check.

Categories