How do I emulate transaction behaviour in PHP with third party APIs? - php

Say for example I am trying to initialize a calendar with recurring events.
try {
$api->createCalendar()
$api->createCalendarSettings()
$api->createRecurringEvent()
} catch (Exception $exception) {
}
I want these 3 calls to act as a unit. If any one of them fails, previously successful actions must be undone. Is there a smart way of doing this other than to call the reverse action for every action in the catch block? The problem with doing that is that I don't know which actions already succeeded and the reverse actions might also blow up.

Related

Prevent queued job from next attempt after specific error

Our jobs collect data from external APIs. If one of the jobs errors out because we unexpectedly reach the API daily limit (i.e. HTTP status 429) it is pointless to retry the job again, or even process any similar jobs, till next day.
Is there a way to prevent the current job to be attempted again after a specific event occurs? Ideally I should be able for example to set a flag in the failed job so I can check it on the next attempt (like suggested here)
Edit: I incorrectly referred to the jobs I didn't want to retry as "failed", however I meant if an error (exception) occurs during the API call. I edited the question.
It turns out the solution I was looking for is obvious: just fail() the job:
public function handle()
{
try {
// execute job stuff and throw a custom
// exception if a certain event occurs
} catch (MyCustomException $e) {
$this->fail($e); // it won't be put back in the queue
}
}

Handle / Log Exceptions that are caught in the whole application

I am currently maintaining a application with a large code base. The application is still under development.
My goal is to log all the exceptions that are thrown in the application. Also the ones that are caught in the try catch block. Due to the large code base I cannot add single lines of code in the catch blocks or create my custom exception class.
The way I tried to solve it or look for a solution are:
Listen for class construction in the whole application
Override the Exception class (can't do this because it is an core php class)
My most recent code that I tried is:
My test exception (dashboardcontroller.php)
try {
throw new \Exception('custom thrown exception');
} catch (\Exception $e) {
Log::info('Exception caught');
}
Exceptions\Handler.php
public function report(Exception $exception)
{
Log::info($exception);
parent::report($exception);
}
Log
[2020-03-10 17:19:09] local.INFO: Exception caught
So the question is: How can I log / handle thrown exceptions that are caught in the try catch block without adding code that requires lot of changes?
Short answer: You can't (as far as I know that is).
Exceptions that are caught are, normally, caught for a reason; they are handled (or: they should be) in the catch-statement (i.e. exception handler). It's the uncaught exceptions that you want to log.
Exceptions should never be used for program-flow, but it does happen anyway and sometimes for good reason:
if (fileExists('foo.txt')) {
fileDelete('foo.txt'); // May result in a race-condition
}
V.s.:
try {
fileDelete('foo.txt')
catch (IOException) {
// NO-OP
}
Another common pattern:
while (true) {
try {
item = queue.Receive(10); // Wait max. 10 seconds
process(item); // Process item
} catch (TimeOutException) {
// Nothing on queue, handle other stuff and then continue waiting...
DoStuff();
}
}
Above patterns are quite common. Again: exceptions shouldn't be used for program-flow and the queue example above surely shouldn't exist but the reality is that it happens. Real world applications, libraries, API's etc. sometimes just work that way and as soon as you're dealing with 3rd party applications, libraries or API's you're in trouble because you can't always change everything to what you would like it to be.
Can you imagine your logfile in the above queue-example with your 'generic application wide catch logger' for an application running 24/7?
When to log an exception (and maybe just as important: what to log) should be considered on a case-by-case basis.

Laravel 5.3 - Laravel DB transaction and notification sends email on failed transaction?

Using the below transaction, I fire an event, which runs the two listeners listed below the event. The issue is when the first event AddQuestionToQuestionsTable fails for any reason, the DB data gets rolled back correctly because of DB::rollback, but the laravel notification email I have setup and fired in
the second listener QuestionAddedNotificationSend gets sent out regardless of the error, so whether there was an error or not.
If there was an error in the transaction, we would not want to send the email. Note: I may add additional listeners that also insert into the DB, so I would need to know how to only fire off the emails when the transaction is successful.
DB::beginTransaction();
try {
event(new LaravelQuestionPosted($question, $user));
// Listeners: AddQuestionToQuestionsTable
// Listeners: QuestionAddedNotificationSend
} catch (\Exception $e) {
DB::rollback();
// something went wrong
}
DB::commit();
Anyone know how to make it work as intended?
You can pass true as the third parameter when dispatching the event with event() to have the listen handling halted when any of the handlers returns a non-null value or throws an exceptions.

How to display notice or success message during the import by Convert Adapter?

How to display notice or success message during the import by the Convert Adapter ?
I can only display error by
Mage::throwException($message)
.
Class responsible for this is Mage_Adminhtml_Block_System_Convert_Profile_Run.
Magento actually have some kind a session message stack. (Something quite similar to the message bags you can find, for example on Symphony).
Those are quite easy to use, just get the session associated to the area you are in and use on of the functions addError, addWarning, addNotice or addSuccess.
Since you are on the data flow module, the session you are looking to is dataflow/session. Take care to get this model via a singleton otherwise, you will end up with odd multiple sessions.
Mage::getSingleton('dataflow/session')->addSuccess($this->__('This will add a success message'));
The other are :
Mage::getSingleton('dataflow/session')->addNotice($this->__('This a notice'));
Mage::getSingleton('dataflow/session')->addWarning($this->__('That a warning'));
Mage::getSingleton('dataflow/session')->addError($this->__('And finally an error'));
And the reason you actually get an error message when you throw an exception, is because somewhere on Magento core, there is a
try {
// the code or class instantiation on which you throw your exception happens here
} catch (Mage_Core_Exception $e) {
Mage::getSingleton('dataflow/session')->addError($e->getMessage());
}

Should my method throw an exception?

My PHP web application is divided into modules, and I use the data model and data mapper patterns. My mapper methods are static, and they exist specifically to interact with the database.
I have the following method:
ModuleMapper::getRecordCountByModuleIdAndSiteId($moduleId, $siteId)
This method is only meant for a set list of modules (I have a good reason). Would it be reasonable to throw an exception if this method is called for a module outside of that set list (eg. "Record count for this module cannot be retrieved.").
try {
$recordCount = ModuleMapper::getRecordCountByModuleIdAndSiteId($moduleId, $siteId);
}
catch (Exception $e) {
// handle exception
}
Another option would be to just return 0.
Depends on how you want to handle errors really. I use zend framework which automatically catches exceptions and forwards them to the error controller for pretty display and logging. Obviously that method will kill processing unless you catch it explicitly. But it simplifies error display and logging.
You could just as well return 0 and do an if statement to display an inline message, or catch the error and display an inline message as well.

Categories