Transactions in codeigniter with multiple tables - mysql

I'm new to transactions in general, but especially with CodeIgniter. I'm using InnoDB and everything, but my transactions aren't rolling back when I want them to. Here's my code (slightly simplified).
$dog_db = $this->load->database('dog', true);
$dog_db->trans_begin();
$dog_id = $this->dogs->insert($new_dog); //Gets primary key of insert
if(!$dog_id)
{
$dog_db->trans_rollback();
throw new Exception('We have had an error trying to add this dog. Please go back and try again.');
}
$new_review['dog_id'] = $dog_id;
$new_review['user_id'] = $user_id;
$new_review['date_added'] = time();
if(!$this->reviews->insert($new_review)) //If the insert fails
{
$dog_db->trans_rollback();
throw new Exception('We have had an error trying to add this dog. Please go back and try again.');
}
//ADD DESCRIPTION
$new_description['description'] = $add_dog['description'];
$new_description['dog_id'] = $dog_id;
$new_description['user_id'] = $user_id;
$new_description['date_added'] = time();
if(!$this->descriptions->insert($new_description))
{
$dog_db->trans_rollback();
throw new Exception('We have had an error trying to add this dog. Please go back and try again.');
}
$dog_db->trans_rollback(); //THIS IS JUST TO SEE IF IT WORKS
throw new Exception('We have had an error trying to add this dog. Please go back and try again.');
$dog_db->trans_commit();
}
catch(Exception $e)
{
echo $e->getMessage();
}
I'm not getting any error messages, but it's not rolling back either. It should roll back at that final trans_rollback right before the commit. My models are all on the "dog" database, so I think that the transaction would carry into the models' functions. Maybe you just can't use models like this. Any help would be greatly appreciated! Thanks!

Well, I know this post is antique, but here's my 2 cents:
I don't think this:
if(!$this->descriptions->insert($new_description))
will work, cause the insert function from CI active record always returns TRUE (succeeding or not). If you are using debug mode, CI will stop on error and throw a screen message to the user, but insert function will still returns TRUE.
So, if you're willing to control transactions "manualy" with CI, you'll have to use something like this:
...
$this->db->trans_begin();
$this->db->insert('FOO');
if ($this->db->trans_status() === FALSE){
$this->db->trans_rollback();
}else{
$this->db->trans_commit();
}
Hope this helps someone...sometime....somewhere

Maybe, it's because you connected using $dog_db, and rolling back nonexisting $booze_db transaction ?(or it's a typo?)

Related

Have to use manual wait() to get CodeceptJS/Puppeteer custom helper to see table(td tr)

I have a codeceptjs/puppeteer project and am building a custom helper for accessing information in tables. I have been able to make this work, but only by putting a two second wait in my test step before calling on the async function in my custom helper class. Given that this is all based on async/await, I have to believe I am just missing something and there is a clean way to do this.
Function from my helper class.
async getRowCount() {
//const browser = this.helpers['Puppeteer'].browser;
const page = this.helpers['Puppeteer'].page;
page.waitForSelector('tbody');
let rowCount = await page.$$eval('tbody tr', rows => rows.length);
return rowCount;
// These work
// page.waitForSelector('a[href="#/site/create"]');
// page.click('a[href="#/site/create"]');
}
My codeceptjs scenario is below.
Scenario.only('Table check ALL', async (I, loginAs) => {
loginAs('bob');
I.say(await I.getRowCount());
I.wait(3);
});
When the code is as shown above, my row count that is returned in always 0.
However, if I put a 1 second wait just before the I.getRowCount() function, then the correct total number of rows for the tbody tr selector is returned.
If anyone can help me understand why this is happening and what I can do to fix it so I don't have to pepper my code with manual wait steps to accommodate these helper functions (core "feature" of codeceptjs), I would greatly appreciate it.
Thank you!
You need to await waitForSelector:
await page.waitForSelector('tbody');
Almost all page methods are returning promises, so you have to await them.

Nodejs Mysql ending

how to check if query ends in nosejd mysql
var query = connection.query(...);
i have used this and worked
query.on('end', function() {
});
but i want another way to do this like
if(query.end){
//do something
}
i know this is silly question but i need this instead of other function to do something else or otherwise i want to know how to call query on end

Windows Phone speech recognition - words that are not in grammar

consider a grammar like this ; speech.Recognizer.Grammars.AddGrammarFromList("answer",new string[] { "Go.","no" });
When I say something else that are not in grammar, she says "sorry didnt catch" and then tries to start it again. Same goes for null input.
What I want is that it should only recognize the words in grammar and for everything else it should just pass the recognition. I don't want to see anything like "sorry didnt catch" and second time recognotion. Any idea ? thanks.
Edit : with try-catch I can avoid from second time recognotion if the word is unknown but now it's waiting too long on "sorry didnt catch" part.
try
{
SpeechRecognizerUI speech = new SpeechRecognizerUI();
speech.Settings.ReadoutEnabled = false;
speech.Settings.ShowConfirmation = false;
speech.Recognizer.Settings.InitialSilenceTimeout = System.TimeSpan.FromSeconds(0.8);
speech.Recognizer.Grammars.AddGrammarFromList("answer", new string[] { "Go.", "no" });
SpeechRecognitionUIResult result = await speech.RecognizeWithUIAsync();
if (result.RecognitionResult.Text == "Go.") { .... }
}
catch
{
..... }
In my opinion, you must build your own UI to avoid this. So you should use SpeechRecognizer and then you can handle the input as you want.
In my case I even created two SpeechRecognizer, on with own Wordlist, the other one with default dictionary. It works like a charm, but I couldn't get it to work with SpeechRecognizerUI.

atg.repository.RemovedItemException when trying to add an item to Repository

I have used InternalProfileFormHandler to add an item InternalProfileRepository. It has successfully added the item(user:iuser310002) to the intended repository. Once added, I have accessed dyn/admin to open the repository and removed item I have just added by using <remove-item item-descriptor="user" id="iuser310002" />. Then I have used invoked InternalProfileFormHandler again to add one more item. However this time, I have got an atg.repository.RemovedItemException saying that Attempt to use an item which has been removed: user:iuser310002.
I'm not sure why it is trying to create a new user with same id as before and even if I did, why an item which is removed should cause this issue. I'm using default idGenerator /atg/dynamo/service/IdGenerator for InternalProfileRepository so I don't think I would be generating same id.
Here is the code that succesfully created item once and failing from second time...
FormHandlerInvoker invoker = new FormHandlerInvoker("/atg/userprofiling/InternalProfileFormHandler", Nucleus.getSystemNucleus());
try {
String paramName;
int paramCounter = 1;
while((paramName = (String) getCurrentTestParams().get("param" + paramCounter)) != null)
{
invoker.addInput(paramName, (String) getCurrentTestParams().get("value" + paramCounter));
paramCounter++;
}
} catch (ServletException e) {
e.printStackTrace();
}
FormHandlerInvocationResult result;
ProfileFormHandler formHandler = null;
try {
result = invoker.invoke();
formHandler =
(ProfileFormHandler)result.getDefaultFormHandler();
formHandler.handleCreate(result.getRequest(), result.getResponse());
}
Following is the exception log... which is caused by formHandler.handleCreate method invocation.
atg.repository.RemovedItemException: Attempt to use an item which has been removed: user:iuser310002
at atg.adapter.gsa.ItemTransactionState.<init>(ItemTransactionState.java:385)
at atg.adapter.gsa.GSAItem.getItemTransactionState(GSAItem.java:2421)
at atg.adapter.gsa.GSAItem.getItemTransactionState(GSAItem.java:2364)
at atg.adapter.gsa.GSAItem.getItemTransactionStateUnchecked(GSAItem.java:2600)
at atg.adapter.gsa.GSAItem.getPropertyValue(GSAItem.java:1511)
at atg.repository.RepositoryItemImpl.getPropertyValue(RepositoryItemImpl.java:151)
at atg.adapter.composite.CompositeItem.createPropertyQuery(CompositeItem.java:739)
at atg.adapter.composite.CompositeItem.getPropertyLinkedItem(CompositeItem.java:630)
at atg.adapter.composite.CompositeItem.getContributingItem(CompositeItem.java:577)
at atg.adapter.composite.CompositeItem.findContributingItem(CompositeItem.java:561)
at atg.adapter.composite.MutableCompositeItem.findContributingItem(MutableCompositeItem.java:971)
at atg.adapter.composite.MutableCompositeItem.getOrCreateContributingItem(MutableCompositeItem.java:985)
at atg.adapter.composite.MutableCompositeItem.setPropertyValue(MutableCompositeItem.java:210)
at atg.userprofiling.ProfileForm.updateProfileAttributes(ProfileForm.java:3761)
at atg.userprofiling.ProfileForm.updateProfileAttributes(ProfileForm.java:3528)
at atg.userprofiling.ProfileForm.createUser(ProfileForm.java:1507)
at atg.userprofiling.ProfileForm.handleCreate(ProfileForm.java:1214)
at atg.userprofiling.ProfileFormHandler.handleCreate(ProfileFormHandler.java:402)
at atg.scenario.userprofiling.ScenarioProfileFormHandler.handleCreate(ScenarioProfileFormHandler.java:599)
at atg.test.steps.CreateInternalUserStep.runTest(CreateInternalUserStep.java:45)
After some digging around, I figured out the reason. Along with creating user, a session is also being created for the same user automatically. The user could be found by going to this path in dyn/admin. /atg/dynamo/servlet/sessiontracking/GenericSessionManager/notdefined/atg/userprofiling/Profile/
Next time when I'm deleting the item in InternalProfileRepository, the item is being deleted successfully but the session object still exists.
When I call the formhandler.handleCreate again, it is checking if the profile object exists. When it found the user, it is trying to update the same user instead of creating a new one. Hence I'm getting the RemovedItemException.
However, I'm not quite sure if that is what is expected.

CakePHP - get last query run

I want to get the last query CakePHP ran. I can't turn debug on in core.php and I can't run the code locally. I need a way to get the last sql query and log it to the error log without effecting the live site. This query is failing but is being run.
something like this would be great:
$this->log($this->ModelName->lastQuery);
Thanks in advance.
For Cake 2.0, the query log is protected so this will work
function getLastQuery() {
$dbo = $this->getDatasource();
$logs = $dbo->getLog();
$lastLog = end($logs['log']);
return $lastLog['query'];
}
Tested in CakePHP v2.3.2
$log = $this->Model->getDataSource()->getLog(false, false);
debug($log);
In CakePHP 1.x, the data you want is accessible in DataSource::_queriesLog. Cake doesn't really provide a getter method for this member, but the underlying language being PHP, nothing stops you from doing the following:
In app/app_model.php:
function getLastQuery()
{
$dbo = $this->getDatasource();
$logs = $dbo->_queriesLog;
return end($logs);
}
You can use this inline.
$dbo = $this->Model->getDatasource();
// store old state
$oldStateFullDebug = $dbo->fullDebug;
// turn fullDebug on
$dbo->fullDebug = true;
// Your code here! eg.
$this->Model->find('all');
// write to logfile
// use print_r with second argument to return a dump of the array
Debugger::log(print_r($dbo->_queriesLog, true));
// restore fullDebug
$dbo->fullDebug = $oldStateFullDebug;
Simple you can use showLog() function
var_dump($this->YourModel->getDataSource()->showLog());
This is a very late answer, i know, but for whoever needs this in the future, you can always restrict setting debug to your IP, For example:
Configure::write('debug', 0);
if($_SERVER["REMOTE_ADDR"] == '192.168.0.100'){
Configure::write('debug', 2); //Enables debugging only for your IP.
}
Combination of Matt's and blavia's solution (works when debug is not 2):
$dbo = $this->Model->getDatasource();
$oldStateFullDebug = $dbo->fullDebug;
$dbo->fullDebug = true;
// find or whatever...
$this->Model->find("all");
$logs = $dbo->getLog();
$lastLog = end($logs['log']);
CakeLog::write("DBLog", $lastLog['query']);
$dbo->fullDebug = $oldStateFullDebug;
Having a quick skim of the book, cakephp api getLog you could turn on logTransaction. Although having not used it, I'm not sure how it will perform.
Otherwise you could experiment with FirePHP and here is the a guide for it,
You might try DebugKit, although off the top of my head I think you do still need debug 2 to get it to work.
Hopefully something might give you a lead. :)
You can use this:
$log = $this->Model->getDataSource()->getLog(false, false);
pr($log);die;
There are two methods to view the query in CakePHP.
Both methods you have to add the below one line in app/Config/core.php
Configure::write('debug', 2);
First methods :
debug($this->getLastQuery());
where you want to get the query add above line and call this function getLastQuery() on same controller using below code
public function getLastQuery() {
$dbo = $this->TModel->getDatasource(); //Here TModel is a model.what table you want to print
$logs = $dbo->getLog();
$lastLog = end($logs['log']);
return $lastLog['query'];
}
second method :
add the below line in the any Elements files.
<?php echo $this->element('sql_dump'); ?>
This will help you.
echo '<pre>';
$log = $this->YOUR_MODEL->getDataSource();
print_r($log);
exit;