Single transaction on multiple model function with CodeIgniter - codeigniter

I need to insert into 2 tables if anything goes wrong while inserting in any of the table I want to rollback commited queries.
I wrote queries inside controller
for example:
$this->db->trans_start();
$this->db->insert_batch('market_users_mapping', $marketData);
$this->db->insert_batch('maincategory_users_mapping', $maincategoryData);
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
throw error
}
This works perfectly. But I think it's not good practice to write queries inside controller. So I did this, called model function and I wrote those insert_batch queries in respective model function.
$this->db->trans_start();
$this->maincategory_model->function_name()
$this->market_model->function_name();
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
throw error
`enter code here`
}
but this didnt work as expected

You changed places of queries in those examples regarding names in case it matters. I think that you can't have tied transactions between different methods (your second example). But you can and should set your DB related code to model.
So make those queries in model:
// controller code
$this->db->trans_start();
$this->maincategory_model->first_function($maincategoryData);
$this->market_model->second_function($marketData);
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
throw error
`enter code here`
}
// Maincategory_model code
public function first_function($maincategoryData)
{
return $this->db->insert_batch('maincategory_users_mapping', $maincategoryData);
}
// Market_model code
public function second_function($marketData)
{
return $this->db->insert_batch('market_users_mapping', $marketData);
}

First shift your db related operation in module and then start transaction.
Sample code in module,
First module :
function insert_data_market_maincategory($marketData,$maincategoryData)
{
$status = TRUE;
$this->db->trans_start();
$this->db->insert_batch('market_users_mapping', $marketData);
$this->second_module_name->maincategoryData($maincategoryData)
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
$status = FALSE;
}
return $status;
}
second module :
function maincategoryData($data)
{
$this->db->insert_batch('table_name', $data);
}
and your controller call this function
sample code in controller,
function inser_user_data()
{
$result = $this->module_name->maincategoryData($marketData,$maincategoryData)
if($result == FALSE)
{
throw error
`enter code here`
}
else
{
//data inserted successfully
}
}

I am using CI 3 and I can use single transaction on multiple model in Controller.
I have tried to insert error data to test if rollback or not, the transaction is successfully rollback.
I did not use Tpojka's anwser but my model methods return true or false. Everything seems okay.

Related

Model events not fire in laravel testing

I'm writing some tests for my model when it was created/updated/deleted. When I test it manually, it worked. But when I test in test file, asserting a event dispatch always fail.
This is my test function:
public function test_booted_method_ran()
{
$model = AcCard::factory()->create();
$this->expectsEvents('eloquent.created: \App\Models\AcCard');
}
This test case always fails. I've tried to inspect expectsEvents and added dd() to check event list:
public function expectsEvents($events)
{
$events = is_array($events) ? $events : func_get_args();
$this->withoutEvents();
$this->beforeApplicationDestroyed(function () use ($events) {
$fired = $this->getFiredEvents($events);
dd($fired); // Added this code line
$this->assertEmpty(
$eventsNotFired = array_diff($events, $fired),
'These expected events were not fired: ['.implode(', ', $eventsNotFired).']'
);
});
return $this;
}
And result is an empty array.
What I tried
I tried to add this code lines above model creating
AcCard::flushEventListeners();
AcCard::boot();
AcCard::booted();
But it doesn't work.

MODELS: Cannot update values in DB

I have this function to update the DB values, but one of the statements is not working. I created a relation to the table called ventas, however, it is not updating it´s value to NULL. This is my code:
public function cambiarEstatusFactura( Request $request){
try{
\DB::beginTransaction();
$factura = FacturaVenta::with('venta')->where( 'id' , $request->id )->first();
// dd('Esto : ' , $factura->venta->factura_uuid);
if( $factura->estatus == 'Timbrada' ){
$factura->estatus = 'Cancelada';
$factura->uuid = NULL;
$factura->venta->factura_uuid = NULL;
$factura->save();
}
\DB::commit();
}catch (Exception $e){
\Alert::error($e->getMessage())->flash();
return redirect('admin/finanzas/factura/venta');
}
}
The statement that is not working is:
$factura->venta->factura_uuid = NULL;
Though the other statements work seamlessly, this seems to have issues but I do not get any error messages.
Variable $factura contains FacturaVenta model and the relation venta brings the model from the table ventas, that's why I try to access to it via $factura->venta and the commented dd('Esto : ' , $factura->venta->factura_uuid); in the code brings the value correctly, that means that it's pointing correctly to the correct table but it's not being updated to NULL.
I just want to update the value from whatever it has to NULL.
Could you help me please?

Laravel 5.7 many to many sync() not working

I have a intermediary table in which I want to save sbj_type_id and difficulty_level_id so I have setup this:
$difficulty_level = DifficultyLevel::find(5);
if ($difficulty_level->sbj_types()->sync($request->hard, false)) {
dd('ok');
}
else {
dd('not ok');
}
Here is my DifficultyLevel.php:
public function sbj_types() {
return $this->belongsToMany('App\SbjType');
}
and here is my SbjType.php:
public function difficulty_levels() {
return $this->hasMany('App\DifficultyLevel');
}
In the above code I have dd('ok') it's returning ok but the database table is empty.
Try to change
return $this->hasMany('App\DifficultyLevel');
to
return $this->belongsToMany('App\DifficultyLevel');
The sync() method takes an array with the id's of the records you want to sync as argument to which you can optionally add intermediate table values. While sync($request->hard, false) doesn't seem to throw an exception in your case, I don't see how this would work.
Try for example:
$difficulty_level->sbj_types()->sync([1,2,3]);
where 1,2,3 are the id's of the sbj_types.
You can read more about syncing here.

laravel transaction called from other controllers

If I will have a function in my BaseController where there is a transaction like this
public static function add_lead_logs($lead_id, $module, $action)
{
DB::beginTransaction();
$lead_log = new LeadLogsModel();
$lead_log->lead_id = $lead_id;
$lead_log->create_by = Session::get('SESS_USER_ID');
$lead_log->module = $module;
$lead_log->action = $action;
if(!$lead_log->save())
{
DB::rollback();
return false;
}
else
{
DB::commit();
return true;
}
}
and then I am calling that function inside a controller, lets say I am updating something
DB::beginTransasction()
$lead = LeadModel::find(1);
$lead->status = '1';
if($lead->save())
{
if($this->add_lead_logs($id,$module,$action))
{
DB::commit();
}
}
else
{
DB::rollback();
}
How can I have just 1 transaction? I am not sure whether this will rollback successfully when there is an error.
Laravel supports nested transactions via a transaction counter. DB::beginTransasction() increments the counter, and DB::commit() and DB::rollback() decrement the counter.
The actual database commit/rollback actions only happen on the outermost transaction call. If you're going to manually call the DB::beginTransasction(), DB::commit(), and DB::rollback() methods, then you need to make sure you're always pairing them.
In the case of your current code, if $lead->save() returns true, but the $this->add_lead_logs() returns false, you're going to enter a codepath that does not call commit/rollback on your outermost transaction. You need to add a rollback in an else condition here.
if($lead->save()) {
if($this->add_lead_logs($id,$module,$action)) {
DB::commit();
} else {
// make sure to rollback if add_lead_logs failed
DB::rollback();
}
} else {
DB::rollback();
}
This will ensure that for all code paths, the transaction will always be committed or rolled back.

Query error handling in CodeIgniter

I am trying to execute a MySQL query using the CI active record library. If the query is malformed, then CI invokes an internal server error 500 and quits without processing the next steps.
I need to roll back all the other queries processed before that error statement, and the roll back is also not happening.. can you help please?
The code snippets is as below:
function dbInsertInformationToDB($data_array)
{
$returnID = "";
$uniqueDataArray = array();
// I prepare a array of values here
$uniqueTableList = filter_unique_tables($data_array[2]);
$this->db->trans_begin();
// inserting is done here
// when there is a query error in $this->db->insert().. it is not rolling back the previous query executed
foreach($uniqueTableList as $table_name)
{
$uniqueDataArray = filterDataArray($data_array,$table_name,2);
$this->db->insert($table_name,$uniqueDataArray);
if ($this->db->_error_message())
{
$error = "I am caught!!";
}
$returnID = $this->db->affected_rows();
}
if ($this->db->trans_status() === FALSE)
{
$this->db->trans_rollback();
}
else
{
$this->db->trans_commit();
}
return "ERROR";
}
Try a try catch block: http://php.net/manual/en/language.exceptions.php

Resources